16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
33 #include "citadel_ipc.h"
34 #include "citadel_decls.h"
36 #include "citadel_dirs.h"
37 #ifdef THREADED_CLIENT
38 pthread_mutex_t rwlock;
42 static SSL_CTX *ssl_ctx;
45 #ifdef THREADED_CLIENT
46 pthread_mutex_t **Critters; /* Things that need locking */
47 #endif /* THREADED_CLIENT */
49 #endif /* HAVE_OPENSSL */
52 #define INADDR_NONE 0xffffffff
55 static void (*status_hook)(char *s) = NULL;
57 void setCryptoStatusHook(void (*hook)(char *s)) {
61 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
62 ipc->network_status_cb = hook;
66 char instant_msgs = 0;
69 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
70 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
72 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
73 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
74 static void ssl_lock(int mode, int n, const char *file, int line);
75 static void endtls(SSL *ssl);
76 #ifdef THREADED_CLIENT
77 static unsigned long id_callback(void);
78 #endif /* THREADED_CLIENT */
79 #endif /* HAVE_OPENSSL */
80 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
81 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
85 * Does nothing. The server should always return 200.
87 int CtdlIPCNoop(CtdlIPC *ipc)
91 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
96 * Does nothing interesting. The server should always return 200
97 * along with your string.
99 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
105 if (!cret) return -2;
107 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110 sprintf(aaa, "ECHO %s", arg);
111 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
118 * Asks the server to close the connecction.
119 * Should always return 200.
121 int CtdlIPCQuit(CtdlIPC *ipc)
123 register int ret = 221; /* Default to successful quit */
127 if (ipc->sock > -1) {
128 CtdlIPC_putline(ipc, "QUIT");
129 CtdlIPC_getline(ipc, aaa);
134 SSL_shutdown(ipc->ssl);
138 shutdown(ipc->sock, 2); /* Close connection; we're dead */
146 * Asks the server to logout. Should always return 200, even if no user
147 * was logged in. The user will not be logged in after this!
149 int CtdlIPCLogout(CtdlIPC *ipc)
155 CtdlIPC_putline(ipc, "LOUT");
156 CtdlIPC_getline(ipc, aaa);
164 * First stage of authentication - pass the username. Returns 300 if the
165 * username is able to log in, with the username correctly spelled in cret.
166 * Returns various 500 error codes if the user doesn't exist, etc.
168 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
173 if (!username) return -2;
174 if (!cret) return -2;
176 aaa = (char *)malloc((size_t)(strlen(username) + 6));
179 sprintf(aaa, "USER %s", username);
180 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
187 * Second stage of authentication - provide password. The server returns
188 * 200 and several arguments in cret relating to the user's account.
190 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
195 if (!passwd) return -2;
196 if (!cret) return -2;
198 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
201 sprintf(aaa, "PASS %s", passwd);
202 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
209 * Second stage of authentication - provide password. The server returns
210 * 200 and several arguments in cret relating to the user's account.
212 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
217 if (!response) return -2;
218 if (!cret) return -2;
220 aaa = (char *)malloc((size_t)(strlen(response) + 6));
223 sprintf(aaa, "PAS2 %s", response);
224 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
231 * Create a new user. This returns 200 plus the same arguments as TryPassword
232 * if selfservice is nonzero, unless there was a problem creating the account.
233 * If selfservice is zero, creates a new user but does not log out the existing
234 * user - intended for use by system administrators to create accounts on
235 * behalf of other users.
237 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
242 if (!username) return -2;
243 if (!cret) return -2;
245 aaa = (char *)malloc((size_t)(strlen(username) + 6));
248 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
249 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
256 * Changes the user's password. Returns 200 if changed, errors otherwise.
258 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
263 if (!passwd) return -2;
264 if (!cret) return -2;
266 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
269 sprintf(aaa, "SETP %s", passwd);
270 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
277 /* Caller must free the march list */
278 /* Room types are defined in enum RoomList; keep these in sync! */
279 /* floor is -1 for all, or floornum */
280 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
283 struct march *march = NULL;
284 static char *proto[] =
285 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
290 if (!listing) return -2;
291 if (*listing) return -2; /* Free the listing first */
292 if (!cret) return -2;
293 /* if (which < 0 || which > 4) return -2; */
294 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
296 sprintf(aaa, "%s %d", proto[which], floor);
297 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
298 if (ret / 100 == 1) {
301 while (bbb && strlen(bbb)) {
304 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
306 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
307 mptr = (struct march *) malloc(sizeof (struct march));
310 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
311 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
312 mptr->march_floor = (char) extract_int(aaa, 2);
313 mptr->march_order = (char) extract_int(aaa, 3);
314 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
315 mptr->march_access = (char) extract_int(aaa, 5);
322 while (mptr2->next != NULL)
336 /* Caller must free the struct ctdluser; caller may pass an existing one */
337 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
341 if (!cret) return -2;
342 if (!uret) return -2;
343 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
344 if (!*uret) return -1;
346 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
347 if (ret / 100 == 2) {
348 uret[0]->USscreenwidth = extract_int(cret, 0);
349 uret[0]->USscreenheight = extract_int(cret, 1);
350 uret[0]->flags = extract_int(cret, 2);
357 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
361 if (!uret) return -2;
362 if (!cret) return -2;
364 sprintf(aaa, "SETU %d|%d|%d",
365 uret->USscreenwidth, uret->USscreenheight,
367 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
372 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
373 struct ctdlipcroom **rret, char *cret)
378 if (!cret) return -2;
379 if (!rret) return -2;
380 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
381 if (!*rret) return -1;
384 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
389 sprintf(aaa, "GOTO %s|%s", room, passwd);
391 aaa = (char *)malloc(strlen(room) + 6);
396 sprintf(aaa, "GOTO %s", room);
398 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
399 if (ret / 100 == 2) {
400 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
401 rret[0]->RRunread = extract_long(cret, 1);
402 rret[0]->RRtotal = extract_long(cret, 2);
403 rret[0]->RRinfoupdated = extract_int(cret, 3);
404 rret[0]->RRflags = extract_int(cret, 4);
405 rret[0]->RRhighest = extract_long(cret, 5);
406 rret[0]->RRlastread = extract_long(cret, 6);
407 rret[0]->RRismailbox = extract_int(cret, 7);
408 rret[0]->RRaide = extract_int(cret, 8);
409 rret[0]->RRnewmail = extract_long(cret, 9);
410 rret[0]->RRfloor = extract_int(cret, 10);
421 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
422 /* whicharg is number of messages, applies to last, first, gt, lt */
423 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
424 const char *mtemplate, unsigned long **mret, char *cret)
427 register unsigned long count = 0;
428 static char *proto[] =
429 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
434 if (!cret) return -2;
435 if (!mret) return -2;
436 if (*mret) return -2;
437 if (which < 0 || which > 6) return -2;
440 sprintf(aaa, "MSGS %s||%d", proto[which],
441 (mtemplate) ? 1 : 0);
443 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
444 (mtemplate) ? 1 : 0);
445 if (mtemplate) count = strlen(mtemplate);
446 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
450 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
453 while (bbb && strlen(bbb)) {
454 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
455 remove_token(bbb, 0, '\n');
456 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
457 sizeof (unsigned long)));
459 (*mret)[count++] = atol(aaa);
471 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
472 struct ctdlipcmessage **mret, char *cret)
478 int multipart_hunting = 0;
479 char multipart_prefix[128];
481 if (!cret) return -1;
482 if (!mret) return -1;
483 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
484 if (!*mret) return -1;
485 if (!msgnum) return -1;
487 strcpy(mret[0]->content_type, "");
488 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
489 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
490 if (ret / 100 == 1) {
492 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
493 while (strlen(bbb) > 4 && bbb[4] == '=') {
494 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
495 remove_token(bbb, 0, '\n');
497 if (!strncasecmp(aaa, "nhdr=yes", 8))
499 else if (!strncasecmp(aaa, "from=", 5))
500 safestrncpy(mret[0]->author, &aaa[5], SIZ);
501 else if (!strncasecmp(aaa, "type=", 5))
502 mret[0]->type = atoi(&aaa[5]);
503 else if (!strncasecmp(aaa, "msgn=", 5))
504 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
505 else if (!strncasecmp(aaa, "subj=", 5))
506 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
507 else if (!strncasecmp(aaa, "rfca=", 5))
508 safestrncpy(mret[0]->email, &aaa[5], SIZ);
509 else if (!strncasecmp(aaa, "hnod=", 5))
510 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
511 else if (!strncasecmp(aaa, "room=", 5))
512 safestrncpy(mret[0]->room, &aaa[5], SIZ);
513 else if (!strncasecmp(aaa, "node=", 5))
514 safestrncpy(mret[0]->node, &aaa[5], SIZ);
515 else if (!strncasecmp(aaa, "rcpt=", 5))
516 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
517 else if (!strncasecmp(aaa, "time=", 5))
518 mret[0]->time = atol(&aaa[5]);
520 /* Multipart/alternative prefix & suffix strings help
521 * us to determine which part we want to download.
523 else if (!strncasecmp(aaa, "pref=", 5)) {
524 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
525 if (!strcasecmp(multipart_prefix,
526 "multipart/alternative")) {
530 else if (!strncasecmp(aaa, "suff=", 5)) {
531 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
532 if (!strcasecmp(multipart_prefix,
533 "multipart/alternative")) {
538 else if (!strncasecmp(aaa, "part=", 5)) {
539 struct parts *ptr, *chain;
541 ptr = (struct parts *)calloc(1, sizeof (struct parts));
544 /* Fill the buffers for the caller */
545 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
546 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
547 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
548 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
549 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
550 ptr->length = extract_long(&aaa[5], 5);
551 if (!mret[0]->attachments)
552 mret[0]->attachments = ptr;
554 chain = mret[0]->attachments;
560 /* Now handle multipart/alternative */
561 if (multipart_hunting > 0) {
562 if ( (!strcasecmp(ptr->mimetype,
564 || (!strcasecmp(ptr->mimetype,
566 strcpy(mret[0]->mime_chosen,
574 /* Eliminate "text\n" */
575 remove_token(bbb, 0, '\n');
577 /* If doing a MIME thing, pull out the extra headers */
580 if (!strncasecmp(bbb, "Content-type:", 13)) {
581 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
582 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
583 striplt(mret[0]->content_type);
585 /* strip out ";charset=" portion. FIXME do something with
586 * the charset (like... convert it) instead of just throwing
589 if (strstr(mret[0]->content_type, ";") != NULL) {
590 strcpy(strstr(mret[0]->content_type, ";"), "");
594 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
595 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
596 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
597 striplt(mret[0]->mime_chosen);
599 remove_token(bbb, 0, '\n');
600 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
601 remove_token(bbb, 0, '\n');
607 /* FIXME: Strip trailing whitespace */
608 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
610 bbb = (char *)realloc(bbb, 1);
620 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
625 if (!cret) return -2;
626 if (!listing) return -2;
627 if (*listing) return -2;
629 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
635 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
639 char *listing = NULL;
642 if (!cret) return -2;
644 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
645 if (ret / 100 == 1) {
648 while (*listing && strlen(listing)) {
649 extract_token(buf, listing, 0, '\n', sizeof buf);
650 remove_token(listing, 0, '\n');
652 case 0: ipc->ServInfo.pid = atoi(buf);
654 case 1: strcpy(ipc->ServInfo.nodename,buf);
656 case 2: strcpy(ipc->ServInfo.humannode,buf);
658 case 3: strcpy(ipc->ServInfo.fqdn,buf);
660 case 4: strcpy(ipc->ServInfo.software,buf);
662 case 5: ipc->ServInfo.rev_level = atoi(buf);
664 case 6: strcpy(ipc->ServInfo.site_location,buf);
666 case 7: strcpy(ipc->ServInfo.sysadm,buf);
668 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
670 case 10: ipc->ServInfo.ok_floors = atoi(buf);
672 case 11: ipc->ServInfo.paging_level = atoi(buf);
674 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
676 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
678 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
684 if (listing) free(listing);
690 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
695 if (!cret) return -2;
696 if (!listing) return -2;
697 if (*listing) return -2;
699 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
705 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
707 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
712 if (!cret) return -2;
715 sprintf(aaa, "SLRP %ld", msgnum);
718 sprintf(aaa, "SLRP HIGHEST");
720 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
726 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
731 if (!cret) return -2;
732 if (!username) return -2;
734 aaa = (char *)malloc(strlen(username) + 6);
737 sprintf(aaa, "INVT %s", username);
738 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
745 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
750 if (!cret) return -1;
751 if (!username) return -1;
753 aaa = (char *)malloc(strlen(username) + 6);
755 sprintf(aaa, "KICK %s", username);
756 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
763 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
767 if (!cret) return -2;
768 if (!qret) return -2;
769 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
770 if (!*qret) return -1;
772 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
773 if (ret / 100 == 2) {
774 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
775 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
776 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
777 qret[0]->QRflags = extract_int(cret, 3);
778 qret[0]->QRfloor = extract_int(cret, 4);
779 qret[0]->QRorder = extract_int(cret, 5);
780 qret[0]->QRdefaultview = extract_int(cret, 6);
781 qret[0]->QRflags2 = extract_int(cret, 7);
788 /* set forget to kick all users out of room */
789 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
794 if (!cret) return -2;
795 if (!qret) return -2;
797 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
798 strlen(qret->QRdirname) + 64);
801 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
802 qret->QRname, qret->QRpasswd, qret->QRdirname,
803 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
804 qret->QRdefaultview, qret->QRflags2);
805 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
812 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
814 if (!cret) return -1;
816 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
821 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
826 if (!cret) return -2;
827 if (!username) return -2;
829 aaa = (char *)malloc(strlen(username) + 6);
832 sprintf(aaa, "SETA %s", username);
833 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
840 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
845 if (!cret) return -2;
848 snprintf(cmd, sizeof cmd,
849 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
850 mr->anonymous, mr->type, mr->subject, mr->author);
851 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
858 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
862 if (!cret) return -2;
863 if (!iret) return -2;
864 if (*iret) return -2;
866 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
871 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
875 if (!cret) return -2;
876 if (!msgnum) return -2;
878 sprintf(aaa, "DELE %ld", msgnum);
879 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
884 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
889 if (!cret) return -2;
890 if (!destroom) return -2;
891 if (!msgnum) return -2;
893 aaa = (char *)malloc(strlen(destroom) + 28);
896 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
897 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
904 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
908 if (!cret) return -2;
910 sprintf(aaa, "KILL %d", for_real);
911 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
916 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
917 const char *password, int floor, char *cret)
922 if (!cret) return -2;
923 if (!roomname) return -2;
926 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
928 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
931 aaa = (char *)malloc(strlen(roomname) + 40);
933 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
936 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
943 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
945 if (!cret) return -2;
947 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
952 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
958 if (!cret) return -2;
959 if (!mret) return -2;
960 if (*mret) return -2;
961 if (!message) return -2;
963 aaa = (char *)malloc(strlen(message) + 6);
966 sprintf(aaa, "MESG %s", message);
967 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
974 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
976 if (!cret) return -2;
978 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
983 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
989 if (!cret) return -2;
990 if (!rret) return -2;
991 if (*rret) return -2;
994 aaa = (char *)malloc(strlen(username) + 6);
996 aaa = (char *)malloc(12);
1000 sprintf(aaa, "GREG %s", username);
1002 sprintf(aaa, "GREG _SELF_");
1003 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1010 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1015 if (!cret) return -2;
1016 if (!username) return -2;
1017 if (axlevel < 0 || axlevel > 7) return -2;
1019 aaa = (char *)malloc(strlen(username) + 17);
1020 if (!aaa) return -1;
1022 sprintf(aaa, "VALI %s|%d", username, axlevel);
1023 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1030 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1034 if (!cret) return -1;
1035 if (!info) return -1;
1037 sprintf(aaa, "EINF %d", for_real);
1038 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1043 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1049 if (!cret) return -1;
1050 if (!listing) return -1;
1051 if (*listing) return -1;
1052 if (!searchstring) return -1;
1054 cmd = malloc(strlen(searchstring) + 10);
1055 sprintf(cmd, "LIST %s", searchstring);
1057 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1064 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1066 if (!cret) return -1;
1067 if (!info) return -1;
1069 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1075 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1079 if (!cret) return -1;
1080 if (!chek) return -1;
1082 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1083 if (ret / 100 == 2) {
1084 chek->newmail = extract_long(cret, 0);
1085 chek->needregis = extract_int(cret, 1);
1086 chek->needvalid = extract_int(cret, 2);
1093 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1098 if (!cret) return -2;
1099 if (!filename) return -2;
1101 aaa = (char *)malloc(strlen(filename) + 6);
1102 if (!aaa) return -1;
1104 sprintf(aaa, "DELF %s", filename);
1105 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1112 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1117 if (!cret) return -2;
1118 if (!filename) return -2;
1119 if (!destroom) return -2;
1121 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1122 if (!aaa) return -1;
1124 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1125 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1132 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1137 if (!cret) return -2;
1138 if (!filename) return -2;
1139 if (!destnode) return -2;
1141 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1142 if (!aaa) return -1;
1144 sprintf(aaa, "NETF %s|%s", filename, destnode);
1145 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1152 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1157 if (!cret) return -1;
1158 if (!listing) return -1;
1159 if (*listing) return -1;
1161 *stamp = CtdlIPCServerTime(ipc, cret);
1163 *stamp = time(NULL);
1164 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1170 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1172 void (*progress_gauge_callback)
1173 (CtdlIPC*, unsigned long, unsigned long),
1182 if (!cret) return -2;
1183 if (!filename) return -2;
1184 if (!buf) return -2;
1185 if (*buf) return -2;
1186 if (ipc->downloading) return -2;
1188 aaa = (char *)malloc(strlen(filename) + 6);
1189 if (!aaa) return -1;
1191 sprintf(aaa, "OPEN %s", filename);
1192 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1194 if (ret / 100 == 2) {
1195 ipc->downloading = 1;
1196 bytes = extract_long(cret, 0);
1197 last_mod = extract_int(cret, 1);
1198 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1200 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1201 progress_gauge_callback, cret);
1203 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1204 progress_gauge_callback, cret);
1207 ret = CtdlIPCEndDownload(ipc, cret);
1209 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1210 filename, mimetype);
1217 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1219 void (*progress_gauge_callback)
1220 (CtdlIPC*, unsigned long, unsigned long),
1230 if (!cret) return -2;
1231 if (!buf) return -2;
1232 if (*buf) return -2;
1233 if (!part) return -2;
1234 if (!msgnum) return -2;
1235 if (ipc->downloading) return -2;
1237 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1238 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1239 if (ret / 100 == 2) {
1240 ipc->downloading = 1;
1241 bytes = extract_long(cret, 0);
1242 last_mod = extract_int(cret, 1);
1243 extract_token(filename, cret, 2, '|', sizeof filename);
1244 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1245 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1246 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1247 ret = CtdlIPCEndDownload(ipc, cret);
1249 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1250 filename, mimetype);
1257 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1258 void (*progress_gauge_callback)
1259 (CtdlIPC*, unsigned long, unsigned long),
1268 if (!cret) return -1;
1269 if (!buf) return -1;
1270 if (*buf) return -1;
1271 if (!filename) return -1;
1272 if (ipc->downloading) return -1;
1274 aaa = (char *)malloc(strlen(filename) + 6);
1275 if (!aaa) return -1;
1277 sprintf(aaa, "OIMG %s", filename);
1278 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1280 if (ret / 100 == 2) {
1281 ipc->downloading = 1;
1282 bytes = extract_long(cret, 0);
1283 last_mod = extract_int(cret, 1);
1284 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1285 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1286 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1287 ret = CtdlIPCEndDownload(ipc, cret);
1289 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1290 filename, mimetype);
1297 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1299 void (*progress_gauge_callback)
1300 (CtdlIPC*, unsigned long, unsigned long),
1306 if (!cret) return -1;
1307 if (!save_as) return -1;
1308 if (!comment) return -1;
1309 if (!path) return -1;
1310 if (!*path) return -1;
1311 if (ipc->uploading) return -1;
1313 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1314 if (!aaa) return -1;
1316 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1317 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1319 if (ret / 100 == 2) {
1321 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1322 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1330 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1331 const char *save_as,
1332 void (*progress_gauge_callback)
1333 (CtdlIPC*, unsigned long, unsigned long),
1339 if (!cret) return -1;
1340 if (!save_as) return -1;
1341 if (!path && for_real) return -1;
1342 if (!*path && for_real) return -1;
1343 if (ipc->uploading) return -1;
1345 aaa = (char *)malloc(strlen(save_as) + 17);
1346 if (!aaa) return -1;
1348 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1349 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1351 if (ret / 100 == 2 && for_real) {
1353 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1354 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1362 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1367 if (!cret) return -2;
1368 if (!username) return -2;
1370 aaa = (char *)malloc(strlen(username) + 6);
1371 if (!aaa) return -1;
1373 sprintf(aaa, "QUSR %s", username);
1374 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1381 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1385 if (!cret) return -2;
1386 if (!listing) return -2;
1387 if (*listing) return -2;
1389 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1394 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1399 if (!cret) return -2;
1400 if (!name) return -2;
1402 sprintf(aaa, "CFLR %s|%d", name, for_real);
1403 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1409 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1413 if (!cret) return -1;
1414 if (floornum < 0) return -1;
1416 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1417 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1422 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1427 if (!cret) return -2;
1428 if (!floorname) return -2;
1429 if (floornum < 0) return -2;
1431 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1432 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1440 * You only need to fill out hostname, the defaults will be used if any of the
1441 * other fields are not set properly.
1443 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1444 int revision, const char *software_name, const char *hostname,
1450 if (developerid < 0 || clientid < 0 || revision < 0 ||
1454 revision = REV_LEVEL - 600;
1455 software_name = "Citadel (libcitadel)";
1457 if (!hostname) return -2;
1459 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1460 if (!aaa) return -1;
1462 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1463 revision, software_name, hostname);
1464 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1471 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1477 if (!cret) return -2;
1478 if (!username) return -2;
1480 aaa = (char *)malloc(strlen(username) + 8);
1481 if (!aaa) return -1;
1484 sprintf(aaa, "SEXP %s|-", username);
1485 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1488 sprintf(aaa, "SEXP %s||", username);
1489 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1497 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1501 if (!cret) return -2;
1502 if (!listing) return -2;
1503 if (*listing) return -2;
1505 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1510 /* mode is 0 = enable, 1 = disable, 2 = status */
1511 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1515 if (!cret) return -2;
1517 sprintf(aaa, "DEXP %d", mode);
1518 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1523 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1525 if (!cret) return -2;
1526 if (!bio) return -2;
1528 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1534 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1540 if (!cret) return -2;
1541 if (!username) return -2;
1542 if (!listing) return -2;
1543 if (*listing) return -2;
1545 aaa = (char *)malloc(strlen(username) + 6);
1546 if (!aaa) return -1;
1548 sprintf(aaa, "RBIO %s", username);
1549 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1556 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1560 if (!cret) return -2;
1561 if (!listing) return -2;
1562 if (*listing) return -2;
1564 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1569 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1573 if (!cret) return -1;
1575 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1576 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1581 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1585 if (!cret) return -1;
1587 sprintf(aaa, "TERM %d", sid);
1588 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1593 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1595 if (!cret) return -1;
1597 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1602 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1606 if (!cret) return -1;
1608 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1609 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1614 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1620 if (!cret) return -2;
1621 if (!text) return -2;
1622 if (!filename) return -2;
1624 aaa = (char *)malloc(strlen(filename) + 6);
1625 if (!aaa) return -1;
1627 sprintf(aaa, "EMSG %s", filename);
1628 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1635 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1640 if (!cret) return -2;
1641 if (!hostname) return -2;
1643 aaa = (char *)malloc(strlen(hostname) + 6);
1644 if (!aaa) return -1;
1646 sprintf(aaa, "HCHG %s", hostname);
1647 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1654 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1659 if (!cret) return -2;
1660 if (!roomname) return -2;
1662 aaa = (char *)malloc(strlen(roomname) + 6);
1663 if (!aaa) return -1;
1665 sprintf(aaa, "RCHG %s", roomname);
1666 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1673 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1678 if (!cret) return -2;
1679 if (!username) return -2;
1681 aaa = (char *)malloc(strlen(username) + 6);
1682 if (!aaa) return -1;
1684 sprintf(aaa, "UCHG %s", username);
1685 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1692 /* This function returns the actual server time reported, or 0 if error */
1693 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1695 register time_t tret;
1698 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1699 if (ret / 100 == 2) {
1700 tret = extract_long(cret, 0);
1709 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1710 struct ctdluser **uret, char *cret)
1715 if (!cret) return -2;
1716 if (!uret) return -2;
1717 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1718 if (!*uret) return -1;
1720 sprintf(aaa, "AGUP %s", who);
1721 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1723 if (ret / 100 == 2) {
1724 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1725 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1726 uret[0]->flags = extract_int(cret, 2);
1727 uret[0]->timescalled = extract_long(cret, 3);
1728 uret[0]->posted = extract_long(cret, 4);
1729 uret[0]->axlevel = extract_int(cret, 5);
1730 uret[0]->usernum = extract_long(cret, 6);
1731 uret[0]->lastcall = extract_long(cret, 7);
1732 uret[0]->USuserpurge = extract_int(cret, 8);
1739 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1744 if (!cret) return -2;
1745 if (!uret) return -2;
1747 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1748 if (!aaa) return -1;
1750 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1751 uret->fullname, uret->password, uret->flags,
1752 uret->timescalled, uret->posted, uret->axlevel,
1753 uret->usernum, uret->lastcall, uret->USuserpurge);
1754 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1761 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1762 /* caller must free the struct ExpirePolicy */
1763 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1764 struct ExpirePolicy **policy, char *cret)
1766 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1770 if (!cret) return -2;
1771 if (!policy) return -2;
1772 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1773 if (!*policy) return -1;
1774 if (which < 0 || which > 3) return -2;
1776 sprintf(cmd, "GPEX %s", proto[which]);
1777 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1778 if (ret / 100 == 2) {
1779 policy[0]->expire_mode = extract_int(cret, 0);
1780 policy[0]->expire_value = extract_int(cret, 1);
1787 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1788 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1789 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1790 struct ExpirePolicy *policy, char *cret)
1793 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1795 if (!cret) return -2;
1796 if (which < 0 || which > 3) return -2;
1797 if (!policy) return -2;
1798 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1799 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1801 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1802 policy->expire_mode, policy->expire_value);
1803 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1808 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1812 if (!cret) return -2;
1813 if (!listing) return -2;
1814 if (*listing) return -2;
1816 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1817 listing, &bytes, cret);
1822 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1824 if (!cret) return -2;
1825 if (!listing) return -2;
1827 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1833 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1834 char **listing, char *cret)
1839 if (!cret) return -2;
1840 if (!mimetype) return -2;
1841 if (!listing) return -2;
1842 if (*listing) return -2;
1844 aaa = malloc(strlen(mimetype) + 13);
1845 if (!aaa) return -1;
1846 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1847 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1848 listing, &bytes, cret);
1853 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1854 const char *listing, char *cret)
1858 if (!cret) return -2;
1859 if (!mimetype) return -2;
1860 if (!listing) return -2;
1862 aaa = malloc(strlen(mimetype) + 13);
1863 if (!aaa) return -1;
1864 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1865 return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1871 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1875 if (!cret) return -2;
1876 if (!listing) return -2;
1877 if (*listing) return -2;
1879 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1880 listing, &bytes, cret);
1885 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1887 if (!cret) return -2;
1888 if (!listing) return -2;
1890 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1896 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1900 if (!cret) return -2;
1901 if (session < 0) return -2;
1903 sprintf(aaa, "REQT %d", session);
1904 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1909 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1913 if (!cret) return -2;
1914 if (msgnum < 0) return -2;
1916 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1917 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1922 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1931 /* New SSL object */
1932 temp_ssl = SSL_new(ssl_ctx);
1934 error_printf("SSL_new failed: %s\n",
1935 ERR_reason_error_string(ERR_get_error()));
1938 /* Pointless flag waving */
1939 #if SSLEAY_VERSION_NUMBER >= 0x0922
1940 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1943 if (!access(EGD_POOL, F_OK))
1946 if (!RAND_status()) {
1947 error_printf("PRNG not properly seeded\n");
1951 /* Associate network connection with SSL object */
1952 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1953 error_printf("SSL_set_fd failed: %s\n",
1954 ERR_reason_error_string(ERR_get_error()));
1958 if (status_hook != NULL)
1959 status_hook("Requesting encryption...\r");
1961 /* Ready to start SSL/TLS */
1963 CtdlIPC_putline(ipc, "STLS");
1964 CtdlIPC_getline(ipc, buf);
1965 if (buf[0] != '2') {
1966 error_printf("Server can't start TLS: %s\n", buf);
1970 r = CtdlIPCGenericCommand(ipc,
1971 "STLS", NULL, 0, NULL, NULL, cret);
1973 error_printf("Server can't start TLS: %s\n", buf);
1978 /* Do SSL/TLS handshake */
1979 if ((a = SSL_connect(temp_ssl)) < 1) {
1980 error_printf("SSL_connect failed: %s\n",
1981 ERR_reason_error_string(ERR_get_error()));
1985 ipc->ssl = temp_ssl;
1987 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1991 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1992 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1993 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1994 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2000 #endif /* HAVE_OPENSSL */
2005 static void endtls(SSL *ssl)
2016 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2020 if (!address) return -2;
2021 if (!cret) return -2;
2023 aaa = (char *)malloc(strlen(address) + 6);
2024 if (!aaa) return -1;
2026 sprintf(aaa, "QDIR %s", address);
2027 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2032 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2036 if (!cret) return -2;
2037 sprintf(aaa, "IPGM %d", secret);
2038 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2043 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2047 if (!cret) return -2;
2048 if (!mret) return -2;
2049 if (*mret) return -2;
2051 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2072 /* ************************************************************************** */
2073 /* Stuff below this line is not for public consumption */
2074 /* ************************************************************************** */
2077 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2079 if (ipc->network_status_cb) ipc->network_status_cb(1);
2080 #ifdef THREADED_CLIENT
2081 pthread_mutex_lock(&(ipc->mutex));
2086 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2088 #ifdef THREADED_CLIENT
2089 pthread_mutex_unlock(&(ipc->mutex));
2091 if (ipc->network_status_cb) ipc->network_status_cb(0);
2095 /* Read a listing from the server up to 000. Append to dest if it exists */
2096 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2105 length = strlen(ret);
2110 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2111 linelength = strlen(aaa);
2112 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2114 strcpy(&ret[length], aaa);
2115 length += linelength;
2116 strcpy(&ret[length++], "\n");
2124 /* Send a listing to the server; generate the ending 000. */
2125 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2129 text = (char *)malloc(strlen(listing) + 6);
2131 strcpy(text, listing);
2132 while (text[strlen(text) - 1] == '\n')
2133 text[strlen(text) - 1] = '\0';
2134 strcat(text, "\n000");
2135 CtdlIPC_putline(ipc, text);
2139 /* Malloc failed but we are committed to send */
2140 /* This may result in extra blanks at the bottom */
2141 CtdlIPC_putline(ipc, text);
2142 CtdlIPC_putline(ipc, "000");
2148 /* Partial read of file from server */
2149 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2151 register size_t len = 0;
2155 if (!cret) return 0;
2156 if (bytes < 1) return 0;
2159 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2160 CtdlIPC_putline(ipc, aaa);
2161 CtdlIPC_getline(ipc, aaa);
2163 strcpy(cret, &aaa[4]);
2165 len = extract_long(&aaa[4], 0);
2166 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2168 /* I know what I'm doing */
2169 serv_read(ipc, ((char *)(*buf) + offset), len);
2171 /* We have to read regardless */
2172 serv_read(ipc, aaa, len);
2176 CtdlIPC_unlock(ipc);
2182 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2186 if (!cret) return -2;
2187 if (!ipc->downloading) return -2;
2189 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2191 ipc->downloading = 0;
2197 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2201 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2202 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2209 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2210 void (*progress_gauge_callback)
2211 (CtdlIPC*, unsigned long, unsigned long),
2214 register size_t len;
2216 if (!cret) return -1;
2217 if (!buf) return -1;
2218 if (*buf) return -1;
2219 if (!ipc->downloading) return -1;
2222 if (progress_gauge_callback)
2223 progress_gauge_callback(ipc, len, bytes);
2224 while (len < bytes) {
2225 register size_t block;
2227 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2233 if (progress_gauge_callback)
2234 progress_gauge_callback(ipc, len, bytes);
2239 /* READ - pipelined */
2240 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2242 void (*progress_gauge_callback)
2243 (CtdlIPC*, unsigned long, unsigned long),
2246 register size_t len;
2247 register int calls; /* How many calls in the pipeline */
2248 register int i; /* iterator */
2251 if (!cret) return -1;
2252 if (!buf) return -1;
2253 if (*buf) return -1;
2254 if (!ipc->downloading) return -1;
2256 *buf = (void *)realloc(*buf, bytes - resume);
2257 if (!*buf) return -1;
2261 if (progress_gauge_callback)
2262 progress_gauge_callback(ipc, len, bytes);
2264 /* How many calls will be in the pipeline? */
2265 calls = (bytes - resume) / 4096;
2266 if ((bytes - resume) % 4096) calls++;
2268 /* Send all requests at once */
2269 for (i = 0; i < calls; i++) {
2270 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2271 CtdlIPC_putline(ipc, aaa);
2274 /* Receive all responses at once */
2275 for (i = 0; i < calls; i++) {
2276 CtdlIPC_getline(ipc, aaa);
2278 strcpy(cret, &aaa[4]);
2280 len = extract_long(&aaa[4], 0);
2281 /* I know what I'm doing */
2282 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2284 if (progress_gauge_callback)
2285 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2287 CtdlIPC_unlock(ipc);
2293 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2298 if (!cret) return -1;
2299 if (!ipc->uploading) return -1;
2301 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2302 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2309 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2310 void (*progress_gauge_callback)
2311 (CtdlIPC*, unsigned long, unsigned long),
2314 register int ret = -1;
2315 register size_t offset = 0;
2321 if (!cret) return -1;
2322 if (!path) return -1;
2323 if (!*path) return -1;
2325 fd = fopen(path, "r");
2328 fseek(fd, 0L, SEEK_END);
2332 if (progress_gauge_callback)
2333 progress_gauge_callback(ipc, 0, bytes);
2335 while (offset < bytes) {
2336 register size_t to_write;
2338 /* Read some data in */
2339 to_write = fread(buf, 1, 4096, fd);
2341 if (feof(fd) || ferror(fd)) break;
2343 sprintf(aaa, "WRIT %d", (int)to_write);
2344 CtdlIPC_putline(ipc, aaa);
2345 CtdlIPC_getline(ipc, aaa);
2346 strcpy(cret, &aaa[4]);
2348 if (aaa[0] == '7') {
2349 to_write = extract_long(&aaa[4], 0);
2351 serv_write(ipc, buf, to_write);
2353 if (progress_gauge_callback)
2354 progress_gauge_callback(ipc, offset, bytes);
2355 /* Detect short reads and back up if needed */
2356 /* offset will never be negative anyway */
2357 fseek(fd, (signed)offset, SEEK_SET);
2362 if (progress_gauge_callback)
2363 progress_gauge_callback(ipc, 1, 1);
2364 return (!ferror(fd) ? ret : -2);
2369 * Generic command method. This method should handle any server command
2370 * except for CHAT. It takes the following arguments:
2372 * ipc The server to speak with
2373 * command Preformatted command to send to server
2374 * to_send A text or binary file to send to server
2375 * (only sent if server requests it)
2376 * bytes_to_send The number of bytes in to_send (required if
2377 * sending binary, optional if sending listing)
2378 * to_receive Pointer to a NULL pointer, if the server
2379 * sends text or binary we will allocate memory
2380 * for the file and stuff it here
2381 * bytes_to_receive If a file is received, we will store its
2383 * proto_response The protocol response. Caller must provide
2384 * this buffer and ensure that it is at least
2385 * 128 bytes in length.
2387 * This function returns a number equal to the protocol response number,
2388 * -1 if an internal error occurred, -2 if caller provided bad values,
2389 * or 0 - the protocol response number if bad values were found during
2390 * the protocol exchange.
2391 * It stores the protocol response string (minus the number) in
2392 * protocol_response as described above. Some commands send additional
2393 * data in this string.
2395 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2396 const char *command, const char *to_send,
2397 size_t bytes_to_send, char **to_receive,
2398 size_t *bytes_to_receive, char *proto_response)
2404 if (!command) return -2;
2405 if (!proto_response) return -2;
2408 if (ipc->ssl) watch_ssl = 1;
2412 CtdlIPC_putline(ipc, command);
2414 CtdlIPC_getline(ipc, proto_response);
2415 if (proto_response[3] == '*')
2417 ret = atoi(proto_response);
2418 strcpy(proto_response, &proto_response[4]);
2419 switch (ret / 100) {
2420 default: /* Unknown, punt */
2422 case 3: /* MORE_DATA */
2424 /* Don't need to do anything */
2426 case 1: /* LISTING_FOLLOWS */
2427 if (to_receive && !*to_receive && bytes_to_receive) {
2428 *to_receive = CtdlIPCReadListing(ipc, NULL);
2429 } else { /* Drain */
2430 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2434 case 4: /* SEND_LISTING */
2436 CtdlIPCSendListing(ipc, to_send);
2438 /* No listing given, fake it */
2439 CtdlIPC_putline(ipc, "000");
2443 case 6: /* BINARY_FOLLOWS */
2444 if (to_receive && !*to_receive && bytes_to_receive) {
2446 extract_long(proto_response, 0);
2447 *to_receive = (char *)
2448 malloc((size_t)*bytes_to_receive);
2452 serv_read(ipc, *to_receive,
2459 drain = extract_long(proto_response, 0);
2460 while (drain > SIZ) {
2461 serv_read(ipc, buf, SIZ);
2464 serv_read(ipc, buf, drain);
2468 case 7: /* SEND_BINARY */
2469 if (to_send && bytes_to_send) {
2470 serv_write(ipc, to_send, bytes_to_send);
2471 } else if (bytes_to_send) {
2472 /* Fake it, send nulls */
2475 fake = bytes_to_send;
2476 memset(buf, '\0', SIZ);
2477 while (fake > SIZ) {
2478 serv_write(ipc, buf, SIZ);
2481 serv_write(ipc, buf, fake);
2483 } /* else who knows? DANGER WILL ROBINSON */
2485 case 8: /* START_CHAT_MODE */
2486 if (!strncasecmp(command, "CHAT", 4)) {
2487 /* Don't call chatmode with generic! */
2488 CtdlIPC_putline(ipc, "/quit");
2491 /* In this mode we send then receive listing */
2493 CtdlIPCSendListing(ipc, to_send);
2495 /* No listing given, fake it */
2496 CtdlIPC_putline(ipc, "000");
2499 if (to_receive && !*to_receive
2500 && bytes_to_receive) {
2501 *to_receive = CtdlIPCReadListing(ipc, NULL);
2502 } else { /* Drain */
2503 while (CtdlIPC_getline(ipc, buf),
2504 strcmp(buf, "000")) ;
2509 case 9: /* ASYNC_MSG */
2510 /* CtdlIPCDoAsync(ret, proto_response); */
2511 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2517 CtdlIPC_unlock(ipc);
2522 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2524 struct hostent *phe;
2525 struct servent *pse;
2526 struct protoent *ppe;
2527 struct sockaddr_in sin;
2530 memset(&sin, 0, sizeof(sin));
2531 sin.sin_family = AF_INET;
2533 pse = getservbyname(service, protocol);
2535 sin.sin_port = pse->s_port;
2537 else if (atoi(service) > 0) {
2538 sin.sin_port = htons(atoi(service));
2541 sin.sin_port = htons(defaultPort);
2543 phe = gethostbyname(host);
2545 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2546 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2549 if ((ppe = getprotobyname(protocol)) == 0) {
2552 if (!strcmp(protocol, "udp")) {
2558 s = socket(PF_INET, type, ppe->p_proto);
2563 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2571 static int uds_connectsock(int *isLocal, char *sockpath)
2573 struct sockaddr_un addr;
2576 memset(&addr, 0, sizeof(addr));
2577 addr.sun_family = AF_UNIX;
2578 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2580 s = socket(AF_UNIX, SOCK_STREAM, 0);
2585 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2596 * input binary data from socket
2598 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2600 unsigned int len, rlen;
2602 #if defined(HAVE_OPENSSL)
2604 serv_read_ssl(ipc, buf, bytes);
2609 while (len < bytes) {
2610 rlen = read(ipc->sock, &buf[len], bytes - len);
2612 connection_died(ipc, 0);
2621 * send binary to server
2623 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2625 unsigned int bytes_written = 0;
2628 #if defined(HAVE_OPENSSL)
2630 serv_write_ssl(ipc, buf, nbytes);
2634 while (bytes_written < nbytes) {
2635 retval = write(ipc->sock, &buf[bytes_written],
2636 nbytes - bytes_written);
2638 connection_died(ipc, 0);
2641 bytes_written += retval;
2648 * input binary data from encrypted connection
2650 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2656 while (len < bytes) {
2657 if (SSL_want_read(ipc->ssl)) {
2658 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2659 error_printf("SSL_write in serv_read:\n");
2660 ERR_print_errors_fp(stderr);
2663 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2667 errval = SSL_get_error(ipc->ssl, rlen);
2668 if (errval == SSL_ERROR_WANT_READ ||
2669 errval == SSL_ERROR_WANT_WRITE) {
2674 Not sure why we'd want to handle these error codes any differently,
2675 but this definitely isn't the way to handle them. Someone must have
2676 naively assumed that we could fall back to unencrypted communications,
2677 but all it does is just recursively blow the stack.
2678 if (errval == SSL_ERROR_ZERO_RETURN ||
2679 errval == SSL_ERROR_SSL) {
2680 serv_read(ipc, &buf[len], bytes - len);
2684 error_printf("SSL_read in serv_read: %s\n",
2685 ERR_reason_error_string(ERR_peek_error()));
2686 connection_died(ipc, 1);
2695 * send binary to server encrypted
2697 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2699 unsigned int bytes_written = 0;
2703 while (bytes_written < nbytes) {
2704 if (SSL_want_write(ipc->ssl)) {
2705 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2706 error_printf("SSL_read in serv_write:\n");
2707 ERR_print_errors_fp(stderr);
2710 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2711 nbytes - bytes_written);
2715 errval = SSL_get_error(ipc->ssl, retval);
2716 if (errval == SSL_ERROR_WANT_READ ||
2717 errval == SSL_ERROR_WANT_WRITE) {
2721 if (errval == SSL_ERROR_ZERO_RETURN ||
2722 errval == SSL_ERROR_SSL) {
2723 serv_write(ipc, &buf[bytes_written],
2724 nbytes - bytes_written);
2727 error_printf("SSL_write in serv_write: %s\n",
2728 ERR_reason_error_string(ERR_peek_error()));
2729 connection_died(ipc, 1);
2732 bytes_written += retval;
2737 static void CtdlIPC_init_OpenSSL(void)
2740 SSL_METHOD *ssl_method;
2743 /* already done init */
2752 SSL_load_error_strings();
2753 SSLeay_add_ssl_algorithms();
2755 /* Set up the SSL context in which we will oeprate */
2756 ssl_method = SSLv23_client_method();
2757 ssl_ctx = SSL_CTX_new(ssl_method);
2759 error_printf("SSL_CTX_new failed: %s\n",
2760 ERR_reason_error_string(ERR_get_error()));
2763 /* Any reasonable cipher we can get */
2764 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2765 error_printf("No ciphers available for encryption\n");
2768 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2770 /* Load DH parameters into the context */
2773 error_printf("Can't allocate a DH object: %s\n",
2774 ERR_reason_error_string(ERR_get_error()));
2777 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2778 error_printf("Can't assign DH_P: %s\n",
2779 ERR_reason_error_string(ERR_get_error()));
2783 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2784 error_printf("Can't assign DH_G: %s\n",
2785 ERR_reason_error_string(ERR_get_error()));
2790 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2793 #ifdef THREADED_CLIENT
2794 /* OpenSSL requires callbacks for threaded clients */
2795 CRYPTO_set_locking_callback(ssl_lock);
2796 CRYPTO_set_id_callback(id_callback);
2798 /* OpenSSL requires us to do semaphores for threaded clients */
2799 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2801 perror("malloc failed");
2804 for (a = 0; a < CRYPTO_num_locks(); a++) {
2805 Critters[a] = malloc(sizeof (pthread_mutex_t));
2807 perror("malloc failed");
2810 pthread_mutex_init(Critters[a], NULL);
2813 #endif /* THREADED_CLIENT */
2817 static void ssl_lock(int mode, int n, const char *file, int line)
2819 #ifdef THREADED_CLIENT
2820 if (mode & CRYPTO_LOCK)
2821 pthread_mutex_lock(Critters[n]);
2823 pthread_mutex_unlock(Critters[n]);
2824 #endif /* THREADED_CLIENT */
2827 #ifdef THREADED_CLIENT
2828 static unsigned long id_callback(void) {
2829 return (unsigned long)pthread_self();
2831 #endif /* THREADED_CLIENT */
2832 #endif /* HAVE_OPENSSL */
2836 * input string from socket - implemented in terms of serv_read()
2838 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2842 /* Read one character at a time. */
2844 serv_read(ipc, &buf[i], 1);
2845 if (buf[i] == '\n' || i == (SIZ-1))
2849 /* If we got a long line, discard characters until the newline. */
2851 while (buf[i] != '\n')
2852 serv_read(ipc, &buf[i], 1);
2854 /* Strip the trailing newline (and carriage return, if present) */
2855 if (buf[i] == 10) buf[i--] = 0;
2856 if (buf[i] == 13) buf[i--] = 0;
2859 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2861 CtdlIPC_getline(ipc, buf);
2865 * send line to server - implemented in terms of serv_write()
2867 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2873 cmd = malloc(len + 2);
2875 /* This requires no extra memory */
2876 serv_write(ipc, buf, len);
2877 serv_write(ipc, "\n", 1);
2879 /* This is network-optimized */
2880 strncpy(cmd, buf, len);
2881 strcpy(cmd + len, "\n");
2882 serv_write(ipc, cmd, len + 1);
2886 ipc->last_command_sent = time(NULL);
2889 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2891 CtdlIPC_putline(ipc, buf);
2898 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2906 ipc = ialloc(CtdlIPC);
2910 #if defined(HAVE_OPENSSL)
2912 CtdlIPC_init_OpenSSL();
2914 #if defined(HAVE_PTHREAD_H)
2915 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2917 ipc->sock = -1; /* Not connected */
2918 ipc->isLocal = 0; /* Not local, of course! */
2919 ipc->downloading = 0;
2921 ipc->last_command_sent = 0L;
2922 ipc->network_status_cb = NULL;
2924 strcpy(cithost, DEFAULT_HOST); /* default host */
2925 strcpy(citport, DEFAULT_PORT); /* default port */
2927 /* Allow caller to supply our values (Windows) */
2928 if (hostbuf && strlen(hostbuf) > 0)
2929 strcpy(cithost, hostbuf);
2930 if (portbuf && strlen(portbuf) > 0)
2931 strcpy(citport, portbuf);
2933 /* Read host/port from command line if present */
2934 for (a = 0; a < argc; ++a) {
2937 } else if (a == 1) {
2938 strcpy(cithost, argv[a]);
2939 } else if (a == 2) {
2940 strcpy(citport, argv[a]);
2942 error_printf("%s: usage: ",argv[0]);
2943 error_printf("%s [host] [port] ",argv[0]);
2950 if ((!strcmp(cithost, "localhost"))
2951 || (!strcmp(cithost, "127.0.0.1"))) {
2955 /* If we're using a unix domain socket we can do a bunch of stuff */
2956 if (!strcmp(cithost, UDS)) {
2957 if (!strcasecmp(citport, DEFAULT_PORT)) {
2958 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2961 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2963 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2964 if (ipc->sock == -1) {
2968 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2969 if (portbuf != NULL) strcpy(portbuf, sockpath);
2973 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2974 if (ipc->sock == -1) {
2978 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2979 if (portbuf != NULL) strcpy(portbuf, citport);
2985 * Disconnect and delete the IPC class (destructor)
2987 void CtdlIPC_delete(CtdlIPC* ipc)
2991 SSL_shutdown(ipc->ssl);
2996 if (ipc->sock > -1) {
2997 shutdown(ipc->sock, 2); /* Close it up */
3005 * Disconnect and delete the IPC class (destructor)
3006 * Also NULLs out the pointer
3008 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3010 CtdlIPC_delete(*pipc);
3016 * return the file descriptor of the server socket so we can select() on it.
3018 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3021 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3028 * return one character
3030 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3033 char CtdlIPC_get(CtdlIPC* ipc)
3038 serv_read(ipc, buf, 1);