1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-09 14:03:12 +02:00

Make client capabilities a modular sub-system, as it should have been since the beginning.

This commit is contained in:
Bram Matthys
2015-07-12 17:10:48 +02:00
parent e0a12b54af
commit 372ce82335
11 changed files with 148 additions and 262 deletions
+1
View File
@@ -627,6 +627,7 @@ extern void delete_classblock(ConfigItem_class *class_ptr);
extern void del_async_connects(void);
extern void make_extbanstr(void);
extern void isupport_init(void);
extern void clicap_init(void);
extern int do_cmd(aClient *cptr, aClient *sptr, char *cmd, int parc, char *parv[]);
extern void create_snomask(aClient *sptr, anUser *user, char *snomask);
extern MODVAR char *me_hash;
+64 -42
View File
@@ -111,6 +111,7 @@ typedef enum ModuleObjectType {
MOBJ_CMODE = 13,
MOBJ_MODDATA = 14,
MOBJ_VALIDATOR = 15,
MOBJ_CLICAP = 16,
} ModuleObjectType;
typedef struct {
@@ -375,34 +376,18 @@ typedef struct _versionflag {
ModuleChild *parents;
} Versionflag;
typedef struct _isupport {
struct _isupport *prev, *next;
char *token;
char *value;
Module *owner;
} Isupport;
#define CLICAP_FLAGS_NONE 0x0
#define CLICAP_FLAGS_STICKY 0x1
#define CLICAP_FLAGS_CLIACK 0x2
typedef struct _ModuleObject {
struct _ModuleObject *prev, *next;
ModuleObjectType type;
union {
Event *event;
Hook *hook;
Command *command;
Hooktype *hooktype;
Versionflag *versionflag;
Snomask *snomask;
Umode *umode;
Cmdoverride *cmdoverride;
Extban *extban;
Callback *callback;
Efunction *efunction;
Isupport *isupport;
Cmode *cmode;
ModDataInfo *moddata;
OperClassValidator *validator;
} object;
} ModuleObject;
typedef struct _clientcapability {
struct _clientcapability *prev, *next;
char *name;
int cap;
int flags;
int (*visible)(void);
Module *owner;
} ClientCapability;
struct _irchook {
Hook *prev, *next;
@@ -454,6 +439,37 @@ struct _hooktype {
char *string;
ModuleChild *parents;
};
typedef struct _isupport {
struct _isupport *prev, *next;
char *token;
char *value;
Module *owner;
} Isupport;
typedef struct _ModuleObject {
struct _ModuleObject *prev, *next;
ModuleObjectType type;
union {
Event *event;
Hook *hook;
Command *command;
Hooktype *hooktype;
Versionflag *versionflag;
Snomask *snomask;
Umode *umode;
Cmdoverride *cmdoverride;
Extban *extban;
Callback *callback;
Efunction *efunction;
Isupport *isupport;
Cmode *cmode;
ModDataInfo *moddata;
OperClassValidator *validator;
ClientCapability *clicap;
} object;
} ModuleObject;
/*
* What we use to keep track internally of the modules
*/
@@ -537,6 +553,12 @@ struct _eventinfo {
/* Huh? Why are those not marked as extern?? -- Syzop */
extern MODVAR Hook *Hooks[MAXHOOKTYPES];
extern MODVAR Hooktype Hooktypes[MAXCUSTOMHOOKS];
extern MODVAR Callback *Callbacks[MAXCALLBACKS], *RCallbacks[MAXCALLBACKS];
extern MODVAR Efunction *Efunctions[MAXEFUNCTIONS];
extern MODVAR ClientCapability *clicaps;
#define EventAdd(name, every, howmany, event, data) EventAddEx(NULL, name, every, howmany, event, data)
Event *EventAddEx(Module *, char *name, long every, long howmany,
vFP event, void *data);
@@ -549,10 +571,7 @@ void EventStatus(aClient *sptr);
void SetupEvents(void);
void LockEventSystem(void);
void UnlockEventSystem(void);
extern MODVAR Hook *Hooks[MAXHOOKTYPES];
extern MODVAR Hooktype Hooktypes[MAXCUSTOMHOOKS];
extern MODVAR Callback *Callbacks[MAXCALLBACKS], *RCallbacks[MAXCALLBACKS];
extern MODVAR Efunction *Efunctions[MAXEFUNCTIONS];
void Module_Init(void);
char *Module_Create(char *path);
@@ -568,23 +587,27 @@ int Module_free(Module *mod);
void *obsd_dlsym(void *handle, char *symbol);
#endif
Versionflag *VersionflagAdd(Module *module, char flag);
void VersionflagDel(Versionflag *vflag, Module *module);
extern Versionflag *VersionflagAdd(Module *module, char flag);
extern void VersionflagDel(Versionflag *vflag, Module *module);
Isupport *IsupportAdd(Module *module, const char *token, const char *value);
void IsupportSetValue(Isupport *isupport, const char *value);
void IsupportDel(Isupport *isupport);
Isupport *IsupportFind(const char *token);
extern Isupport *IsupportAdd(Module *module, const char *token, const char *value);
extern void IsupportSetValue(Isupport *isupport, const char *value);
extern void IsupportDel(Isupport *isupport);
extern Isupport *IsupportFind(const char *token);
extern ClientCapability *ClientCapabilityFind(const char *token);
extern ClientCapability *ClientCapabilityAdd(Module *module, ClientCapability *clicap_request);
extern void ClientCapabilityDel(ClientCapability *clicap);
#define HookAdd(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, func, NULL, NULL)
#define HookAddVoid(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, func, NULL)
#define HookAddPChar(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, NULL, func)
Hook *HookAddMain(Module *module, int hooktype, int priority, int (*intfunc)(), void (*voidfunc)(), char *(*pcharfunc)());
Hook *HookDel(Hook *hook);
extern Hook *HookAddMain(Module *module, int hooktype, int priority, int (*intfunc)(), void (*voidfunc)(), char *(*pcharfunc)());
extern Hook *HookDel(Hook *hook);
Hooktype *HooktypeAdd(Module *module, char *string, int *type);
void HooktypeDel(Hooktype *hooktype, Module *module);
extern Hooktype *HooktypeAdd(Module *module, char *string, int *type);
extern void HooktypeDel(Hooktype *hooktype, Module *module);
#define RunHook0(hooktype) do { Hook *h; for (h = Hooks[hooktype]; h; h = h->next)(*(h->func.intfunc))(); } while(0)
#define RunHook(hooktype,x) do { Hook *h; for (h = Hooks[hooktype]; h; h = h->next) (*(h->func.intfunc))(x); } while(0)
@@ -716,7 +739,6 @@ extern char *moddata_client_get(aClient *acptr, char *varname);
#define HOOKTYPE_PACKET 51
#define HOOKTYPE_HANDSHAKE 52
#define HOOKTYPE_AWAY 53
#define HOOKTYPE_CAPLIST 54
#define HOOKTYPE_INVITE 55
#define HOOKTYPE_CAN_JOIN 56
#define HOOKTYPE_CAN_SEND 57
+4 -1
View File
@@ -157,7 +157,7 @@ EXP_OBJ_FILES=SRC/CHANNEL.OBJ SRC/SEND.OBJ SRC/SOCKET.OBJ \
SRC/S_SVS.OBJ SRC/EVENTS.OBJ SRC/UMODES.OBJ SRC/AUTH.OBJ SRC/CIDR.OBJ SRC/SSL.OBJ \
SRC/RANDOM.OBJ SRC/EXTCMODES.OBJ SRC/MODDATA.OBJ SRC/UID.OBJ SRC/MEMPOOL.OBJ \
SRC/S_DISPATCH.OBJ SRC/MD5.OBJ SRC/API-ISUPPORT.OBJ SRC/API-COMMAND.OBJ \
SRC/EXTBANS.OBJ SRC/TIMESYNCH.OBJ SRC/CRYPT_BLOWFISH.OBJ \
SRC/API-CLICAP.OBJ SRC/EXTBANS.OBJ SRC/TIMESYNCH.OBJ SRC/CRYPT_BLOWFISH.OBJ \
SRC/OPERCLASS.OBJ SRC/UPDCONF.OBJ $(CURLOBJ)
OBJ_FILES=$(EXP_OBJ_FILES) SRC/GUI.OBJ SRC/SERVICE.OBJ SRC/DEBUG.OBJ SRC/RTF.OBJ \
@@ -467,6 +467,9 @@ src/api-isupport.obj: src/api-isupport.c $(INCLUDES)
src/api-command.obj: src/api-command.c $(INCLUDES)
$(CC) $(CFLAGS) src/api-command.c
src/api-clicap.obj: src/api-clicap.c $(INCLUDES)
$(CC) $(CFLAGS) src/api-clicap.c
src/ssl.obj: src/ssl.c $(INCLUDES)
$(CC) $(CFLAGS) src/ssl.c
+4 -1
View File
@@ -28,7 +28,7 @@ OBJS=timesynch.o res.o s_bsd.o auth.o aln.o channel.o cloak.o crule.o dbuf.o \
s_misc.o s_numeric.o s_serv.o s_svs.o $(STRTOUL) socket.o \
ssl.o s_user.o charsys.o scache.o send.o support.o umodes.o \
version.o whowas.o cidr.o random.o extcmodes.o moddata.o uid.o \
extbans.o api-isupport.o api-command.o md5.o crypt_blowfish.o \
extbans.o api-isupport.o api-command.o api-clicap.o md5.o crypt_blowfish.o \
updconf.o $(URL)
SRC=$(OBJS:%.o=%.c)
@@ -251,6 +251,9 @@ api-command.o: api-command.c $(INCLUDES)
api-isupport.o: api-isupport.c $(INCLUDES)
$(CC) $(CFLAGS) -c api-isupport.c
api-clicap.o: api-clicap.c $(INCLUDES)
$(CC) $(CFLAGS) -c api-clicap.c
crypt_blowfish.o: crypt_blowfish.c $(INCLUDES)
$(CC) $(CFLAGS) -c crypt_blowfish.c
+1
View File
@@ -1438,6 +1438,7 @@ int InitwIRCD(int argc, char *argv[])
extcmodes_check_for_changes();
make_extbanstr();
isupport_init();
clicap_init();
if (!find_Command_simple("AWAY") /*|| !find_Command_simple("KILL") ||
!find_Command_simple("OPER") || !find_Command_simple("PING")*/)
{
+3
View File
@@ -616,6 +616,9 @@ void FreeModObj(ModuleObject *obj, Module *m)
else if (obj->type == MOBJ_VALIDATOR) {
OperClassValidatorDel(obj->object.validator);
}
else if (obj->type == MOBJ_CLICAP) {
ClientCapabilityDel(obj->object.clicap);
}
}
void Unload_all_loaded_modules(void)
+9 -39
View File
@@ -17,29 +17,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "proto.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef _WIN32
#include "version.h"
#endif
#include "m_cap.h"
#include "unrealircd.h"
ModuleHeader MOD_HEADER(cap_invitenotify)
= {
@@ -51,40 +29,32 @@ ModuleHeader MOD_HEADER(cap_invitenotify)
};
static void cap_invitenotify_caplist(struct list_head *head)
{
ClientCapability *cap;
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("invite-notify");
cap->cap = PROTO_INVITENOTIFY;
clicap_append(head, cap);
}
static void cap_invitenotify_invite(aClient *from, aClient *to, aChannel *chptr)
{
sendto_channel_butone_with_capability(from, PROTO_INVITENOTIFY,
from, chptr, "INVITE %s :%s", to->name, chptr->chname);
}
/* This is called on module init, before Server Ready */
MOD_INIT(cap_invitenotify)
{
MARK_AS_OFFICIAL_MODULE(modinfo);
ClientCapability cap;
MARK_AS_OFFICIAL_MODULE(modinfo);
HookAddVoid(modinfo->handle, HOOKTYPE_INVITE, 0, cap_invitenotify_invite);
HookAddVoid(modinfo->handle, HOOKTYPE_CAPLIST, 0, cap_invitenotify_caplist);
return MOD_SUCCESS;
memset(&cap, 0, sizeof(cap));
cap.name = "invite-notify";
cap.cap = PROTO_INVITENOTIFY;
ClientCapabilityAdd(modinfo->handle, &cap);
return MOD_SUCCESS;
}
/* Is first run when server is 100% ready */
MOD_LOAD(cap_invitenotify)
{
return MOD_SUCCESS;
}
/* Called when module is unloaded */
MOD_UNLOAD(cap_invitenotify)
{
return MOD_SUCCESS;
+48 -98
View File
@@ -20,34 +20,11 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "proto.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef _WIN32
#include "version.h"
#endif
#include "m_cap.h"
#include "unrealircd.h"
typedef int (*bqcmp)(const void *, const void *);
DLLFUNC int m_cap(aClient *cptr, aClient *sptr, int parc, char *parv[]);
static struct list_head clicap_list;
CMD_FUNC(m_cap);
#define MSG_CAP "CAP"
@@ -60,36 +37,44 @@ ModuleHeader MOD_HEADER(m_cap)
NULL
};
static void clicap_build_list(void)
MOD_INIT(m_cap)
{
ClientCapability *cap;
ClientCapability c;
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, MSG_CAP, m_cap, MAXPARA, M_UNREGISTERED|M_USER);
INIT_LIST_HEAD(&clicap_list);
memset(&c, 0, sizeof(c));
c.name = "account-notify";
c.cap = PROTO_ACCOUNT_NOTIFY;
ClientCapabilityAdd(modinfo->handle, &c);
memset(&c, 0, sizeof(c));
c.name = "away-notify";
c.cap = PROTO_AWAY_NOTIFY;
ClientCapabilityAdd(modinfo->handle, &c);
/* ADD BUILTINS */
memset(&c, 0, sizeof(c));
c.name = "multi-prefix";
c.cap = PROTO_NAMESX;
ClientCapabilityAdd(modinfo->handle, &c);
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("account-notify");
cap->cap = PROTO_ACCOUNT_NOTIFY;
clicap_append(&clicap_list, cap);
memset(&c, 0, sizeof(c));
c.name = "userhost-in-names";
c.cap = PROTO_UHNAMES;
ClientCapabilityAdd(modinfo->handle, &c);
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("away-notify");
cap->cap = PROTO_AWAY_NOTIFY;
clicap_append(&clicap_list, cap);
return MOD_SUCCESS;
}
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("multi-prefix");
cap->cap = PROTO_NAMESX;
clicap_append(&clicap_list, cap);
MOD_LOAD(m_cap)
{
return MOD_SUCCESS;
}
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("userhost-in-names");
cap->cap = PROTO_UHNAMES;
clicap_append(&clicap_list, cap);
RunHook(HOOKTYPE_CAPLIST, &clicap_list);
MOD_UNLOAD(m_cap)
{
return MOD_SUCCESS;
}
static ClientCapability *clicap_find(const char *data, int *negate, int *finished)
@@ -102,7 +87,7 @@ static ClientCapability *clicap_find(const char *data, int *negate, int *finishe
*negate = 0;
if (data)
{
{
strlcpy(buf, data, sizeof(buf));
p = buf;
}
@@ -133,23 +118,11 @@ static ClientCapability *clicap_find(const char *data, int *negate, int *finishe
if((s = strchr(p, ' ')))
*s++ = '\0';
if (!stricmp(p, "sasl") && (!SASL_SERVER || !find_server(SASL_SERVER, NULL)))
return NULL; /* hack: if SASL is disabled or server not online, then pretend it does not exist. -- Syzop */
list_for_each_entry2(cap, ClientCapability, &clicap_list, caplist_node)
{
if (!stricmp(cap->name, p))
{
if (s)
p = s;
else
*finished = 1;
return cap;
}
}
return NULL;
cap = ClientCapabilityFind(p);
if (!s)
*finished = 1;
return cap;
}
static void clicap_generate(aClient *sptr, const char *subcmd, int flags, int clear)
@@ -173,7 +146,7 @@ static void clicap_generate(aClient *sptr, const char *subcmd, int flags, int cl
return;
}
list_for_each_entry2(cap, ClientCapability, &clicap_list, caplist_node)
for (cap = clicaps; cap; cap = cap->next)
{
if (flags)
{
@@ -271,10 +244,10 @@ static int cap_ack(aClient *sptr, const char *arg)
static int cap_clear(aClient *sptr, const char *arg)
{
clicap_generate(sptr, "ACK", sptr->proto ? sptr->proto : -1, 1);
clicap_generate(sptr, "ACK", sptr->proto ? sptr->proto : -1, 1);
sptr->proto = 0;
return 0;
sptr->proto = 0;
return 0;
}
static int cap_end(aClient *sptr, const char *arg)
@@ -292,8 +265,8 @@ static int cap_end(aClient *sptr, const char *arg)
static int cap_list(aClient *sptr, const char *arg)
{
clicap_generate(sptr, "LIST", sptr->proto ? sptr->proto : -1, 0);
return 0;
clicap_generate(sptr, "LIST", sptr->proto ? sptr->proto : -1, 0);
return 0;
}
static int cap_ls(aClient *sptr, const char *arg)
@@ -411,10 +384,10 @@ static struct clicap_cmd clicap_cmdtable[] = {
static int clicap_cmd_search(const char *command, struct clicap_cmd *entry)
{
return strcmp(command, entry->cmd);
return strcmp(command, entry->cmd);
}
DLLFUNC int m_cap(aClient *cptr, aClient *sptr, int parc, char *parv[])
CMD_FUNC(m_cap)
{
struct clicap_cmd *cmd;
@@ -440,7 +413,7 @@ DLLFUNC int m_cap(aClient *cptr, aClient *sptr, int parc, char *parv[])
if(!(cmd = bsearch(parv[1], clicap_cmdtable,
sizeof(clicap_cmdtable) / sizeof(struct clicap_cmd),
sizeof(struct clicap_cmd), (bqcmp) clicap_cmd_search)))
{
{
sendto_one(sptr, err_str(ERR_INVALIDCAPCMD),
me.name, BadPtr(sptr->name) ? "*" : sptr->name,
parv[1]);
@@ -451,26 +424,3 @@ DLLFUNC int m_cap(aClient *cptr, aClient *sptr, int parc, char *parv[])
return (cmd->func)(sptr, parv[2]);
}
/* This is called on module init, before Server Ready */
MOD_INIT(m_cap)
{
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, MSG_CAP, m_cap, MAXPARA, M_UNREGISTERED|M_USER);
return MOD_SUCCESS;
}
/* Is first run when server is 100% ready */
MOD_LOAD(m_cap)
{
clicap_build_list();
return MOD_SUCCESS;
}
/* Called when module is unloaded */
MOD_UNLOAD(m_cap)
{
// XXX free cap list
return MOD_SUCCESS;
}
-46
View File
@@ -1,46 +0,0 @@
/*
* IRC - Internet Relay Chat, src/modules/m_cap.h
* (C) 2012 The UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef M_CAP_H
#define M_CAP_H
#include "list.h"
typedef struct _clicap {
struct list_head caplist_node;
const char *name;
int cap;
int flags;
} ClientCapability;
#define CLICAP_FLAGS_NONE 0x0
#define CLICAP_FLAGS_STICKY 0x1
#define CLICAP_FLAGS_CLIACK 0x2
static inline void
clicap_append(struct list_head *head, ClientCapability *child)
{
INIT_LIST_HEAD(&child->caplist_node);
list_add_tail(&child->caplist_node, head);
}
#endif
+7 -19
View File
@@ -41,7 +41,6 @@
#ifdef _WIN32
#include "version.h"
#endif
#include "m_cap.h"
#define MSG_AUTHENTICATE "AUTHENTICATE"
@@ -294,23 +293,10 @@ static int abort_sasl(struct Client *cptr)
return 0;
}
static void m_sasl_caplist(struct list_head *head)
{
ClientCapability *cap;
/* if SASL is disabled or server not online, then pretend it does not exist. -- Syzop */
if (!SASL_SERVER || !find_server(SASL_SERVER, NULL))
return;
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("sasl");
cap->cap = PROTO_SASL;
clicap_append(head, cap);
}
/* This is called on module init, before Server Ready */
MOD_INIT(m_sasl)
{
ClientCapability cap;
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, MSG_SASL, m_sasl, MAXPARA, M_USER|M_SERVER);
@@ -320,18 +306,20 @@ MOD_INIT(m_sasl)
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, abort_sasl);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, abort_sasl);
HookAddVoid(modinfo->handle, HOOKTYPE_CAPLIST, 0, m_sasl_caplist);
memset(&cap, 0, sizeof(cap));
cap.name = "sasl";
cap.cap = PROTO_SASL;
// todo: validate function
ClientCapabilityAdd(modinfo->handle, &cap);
return MOD_SUCCESS;
}
/* Is first run when server is 100% ready */
MOD_LOAD(m_sasl)
{
return MOD_SUCCESS;
}
/* Called when module is unloaded */
MOD_UNLOAD(m_sasl)
{
return MOD_SUCCESS;
+7 -16
View File
@@ -40,7 +40,6 @@
#ifdef _WIN32
#include "version.h"
#endif
#include "m_cap.h"
DLLFUNC CMD_FUNC(m_starttls);
@@ -55,15 +54,18 @@ ModuleHeader MOD_HEADER(m_starttls)
NULL
};
static void m_starttls_caplist(struct list_head *head);
MOD_INIT(m_starttls)
{
ClientCapability cap;
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, MSG_STARTTLS, m_starttls, MAXPARA, M_UNREGISTERED|M_ANNOUNCE);
HookAddVoid(modinfo->handle, HOOKTYPE_CAPLIST, 0, m_starttls_caplist);
memset(&cap, 0, sizeof(cap));
cap.name = "tls";
cap.cap = PROTO_STARTTLS;
ClientCapabilityAdd(modinfo->handle, &cap);
MARK_AS_OFFICIAL_MODULE(modinfo);
return MOD_SUCCESS;
}
@@ -77,17 +79,6 @@ MOD_UNLOAD(m_starttls)
return MOD_SUCCESS;
}
static void m_starttls_caplist(struct list_head *head)
{
ClientCapability *cap;
cap = MyMallocEx(sizeof(ClientCapability));
cap->name = strdup("tls");
cap->cap = PROTO_STARTTLS,
clicap_append(head, cap); /* this is wrong.. head? and unfreed */
/* todo: free */
}
DLLFUNC CMD_FUNC(m_starttls)
{
if (!MyConnect(sptr) || !IsUnknown(sptr))