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: ", 14)) {
581 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
582 strcpy(mret[0]->content_type,
583 &mret[0]->content_type[14]);
584 striplt(mret[0]->content_type);
586 /* strip out ";charset=" portion. FIXME do something with
587 * the charset (like... convert it) instead of just throwing
590 if (strstr(mret[0]->content_type, ";") != NULL) {
591 strcpy(strstr(mret[0]->content_type, ";"), "");
595 remove_token(bbb, 0, '\n');
596 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
597 remove_token(bbb, 0, '\n');
603 /* FIXME: Strip trailing whitespace */
604 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
606 bbb = (char *)realloc(bbb, 1);
616 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
621 if (!cret) return -2;
622 if (!listing) return -2;
623 if (*listing) return -2;
625 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
631 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
635 char *listing = NULL;
638 if (!cret) return -2;
640 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
641 if (ret / 100 == 1) {
644 while (*listing && strlen(listing)) {
645 extract_token(buf, listing, 0, '\n', sizeof buf);
646 remove_token(listing, 0, '\n');
648 case 0: ipc->ServInfo.pid = atoi(buf);
650 case 1: strcpy(ipc->ServInfo.nodename,buf);
652 case 2: strcpy(ipc->ServInfo.humannode,buf);
654 case 3: strcpy(ipc->ServInfo.fqdn,buf);
656 case 4: strcpy(ipc->ServInfo.software,buf);
658 case 5: ipc->ServInfo.rev_level = atoi(buf);
660 case 6: strcpy(ipc->ServInfo.site_location,buf);
662 case 7: strcpy(ipc->ServInfo.sysadm,buf);
664 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
666 case 10: ipc->ServInfo.ok_floors = atoi(buf);
668 case 11: ipc->ServInfo.paging_level = atoi(buf);
670 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
672 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
674 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
680 if (listing) free(listing);
686 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
691 if (!cret) return -2;
692 if (!listing) return -2;
693 if (*listing) return -2;
695 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
701 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
703 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
708 if (!cret) return -2;
711 sprintf(aaa, "SLRP %ld", msgnum);
714 sprintf(aaa, "SLRP HIGHEST");
716 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
722 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
727 if (!cret) return -2;
728 if (!username) return -2;
730 aaa = (char *)malloc(strlen(username) + 6);
733 sprintf(aaa, "INVT %s", username);
734 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
741 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
746 if (!cret) return -1;
747 if (!username) return -1;
749 aaa = (char *)malloc(strlen(username) + 6);
751 sprintf(aaa, "KICK %s", username);
752 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
759 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
763 if (!cret) return -2;
764 if (!qret) return -2;
765 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
766 if (!*qret) return -1;
768 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
769 if (ret / 100 == 2) {
770 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
771 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
772 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
773 qret[0]->QRflags = extract_int(cret, 3);
774 qret[0]->QRfloor = extract_int(cret, 4);
775 qret[0]->QRorder = extract_int(cret, 5);
776 qret[0]->QRdefaultview = extract_int(cret, 6);
777 qret[0]->QRflags2 = extract_int(cret, 7);
784 /* set forget to kick all users out of room */
785 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
790 if (!cret) return -2;
791 if (!qret) return -2;
793 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
794 strlen(qret->QRdirname) + 64);
797 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
798 qret->QRname, qret->QRpasswd, qret->QRdirname,
799 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
800 qret->QRdefaultview, qret->QRflags2);
801 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
808 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
810 if (!cret) return -1;
812 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
817 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
822 if (!cret) return -2;
823 if (!username) return -2;
825 aaa = (char *)malloc(strlen(username) + 6);
828 sprintf(aaa, "SETA %s", username);
829 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
836 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
841 if (!cret) return -2;
844 snprintf(cmd, sizeof cmd,
845 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
846 mr->anonymous, mr->type, mr->subject, mr->author);
847 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
854 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
858 if (!cret) return -2;
859 if (!iret) return -2;
860 if (*iret) return -2;
862 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
867 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
871 if (!cret) return -2;
872 if (!msgnum) return -2;
874 sprintf(aaa, "DELE %ld", msgnum);
875 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
880 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
885 if (!cret) return -2;
886 if (!destroom) return -2;
887 if (!msgnum) return -2;
889 aaa = (char *)malloc(strlen(destroom) + 28);
892 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
893 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
900 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
904 if (!cret) return -2;
906 sprintf(aaa, "KILL %d", for_real);
907 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
912 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
913 const char *password, int floor, char *cret)
918 if (!cret) return -2;
919 if (!roomname) return -2;
922 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
924 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
927 aaa = (char *)malloc(strlen(roomname) + 40);
929 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
932 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
939 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
941 if (!cret) return -2;
943 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
948 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
954 if (!cret) return -2;
955 if (!mret) return -2;
956 if (*mret) return -2;
957 if (!message) return -2;
959 aaa = (char *)malloc(strlen(message) + 6);
962 sprintf(aaa, "MESG %s", message);
963 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
970 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
972 if (!cret) return -2;
974 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
979 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
985 if (!cret) return -2;
986 if (!rret) return -2;
987 if (*rret) return -2;
990 aaa = (char *)malloc(strlen(username) + 6);
992 aaa = (char *)malloc(12);
996 sprintf(aaa, "GREG %s", username);
998 sprintf(aaa, "GREG _SELF_");
999 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1006 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1011 if (!cret) return -2;
1012 if (!username) return -2;
1013 if (axlevel < 0 || axlevel > 7) return -2;
1015 aaa = (char *)malloc(strlen(username) + 17);
1016 if (!aaa) return -1;
1018 sprintf(aaa, "VALI %s|%d", username, axlevel);
1019 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1026 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1030 if (!cret) return -1;
1031 if (!info) return -1;
1033 sprintf(aaa, "EINF %d", for_real);
1034 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1039 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1045 if (!cret) return -1;
1046 if (!listing) return -1;
1047 if (*listing) return -1;
1048 if (!searchstring) return -1;
1050 cmd = malloc(strlen(searchstring) + 10);
1051 sprintf(cmd, "LIST %s", searchstring);
1053 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1060 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1062 if (!cret) return -1;
1063 if (!info) return -1;
1065 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1071 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1075 if (!cret) return -1;
1076 if (!chek) return -1;
1078 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1079 if (ret / 100 == 2) {
1080 chek->newmail = extract_long(cret, 0);
1081 chek->needregis = extract_int(cret, 1);
1082 chek->needvalid = extract_int(cret, 2);
1089 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1094 if (!cret) return -2;
1095 if (!filename) return -2;
1097 aaa = (char *)malloc(strlen(filename) + 6);
1098 if (!aaa) return -1;
1100 sprintf(aaa, "DELF %s", filename);
1101 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1108 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1113 if (!cret) return -2;
1114 if (!filename) return -2;
1115 if (!destroom) return -2;
1117 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1118 if (!aaa) return -1;
1120 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1121 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1128 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1133 if (!cret) return -2;
1134 if (!filename) return -2;
1135 if (!destnode) return -2;
1137 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1138 if (!aaa) return -1;
1140 sprintf(aaa, "NETF %s|%s", filename, destnode);
1141 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1148 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1153 if (!cret) return -1;
1154 if (!listing) return -1;
1155 if (*listing) return -1;
1157 *stamp = CtdlIPCServerTime(ipc, cret);
1159 *stamp = time(NULL);
1160 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1166 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1168 void (*progress_gauge_callback)
1169 (CtdlIPC*, unsigned long, unsigned long),
1178 if (!cret) return -2;
1179 if (!filename) return -2;
1180 if (!buf) return -2;
1181 if (*buf) return -2;
1182 if (ipc->downloading) return -2;
1184 aaa = (char *)malloc(strlen(filename) + 6);
1185 if (!aaa) return -1;
1187 sprintf(aaa, "OPEN %s", filename);
1188 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1190 if (ret / 100 == 2) {
1191 ipc->downloading = 1;
1192 bytes = extract_long(cret, 0);
1193 last_mod = extract_int(cret, 1);
1194 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1196 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1197 progress_gauge_callback, cret);
1199 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1200 progress_gauge_callback, cret);
1203 ret = CtdlIPCEndDownload(ipc, cret);
1205 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1206 filename, mimetype);
1213 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1215 void (*progress_gauge_callback)
1216 (CtdlIPC*, unsigned long, unsigned long),
1226 if (!cret) return -2;
1227 if (!buf) return -2;
1228 if (*buf) return -2;
1229 if (!part) return -2;
1230 if (!msgnum) return -2;
1231 if (ipc->downloading) return -2;
1233 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1234 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1235 if (ret / 100 == 2) {
1236 ipc->downloading = 1;
1237 bytes = extract_long(cret, 0);
1238 last_mod = extract_int(cret, 1);
1239 extract_token(filename, cret, 2, '|', sizeof filename);
1240 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1241 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1242 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1243 ret = CtdlIPCEndDownload(ipc, cret);
1245 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1246 filename, mimetype);
1253 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1254 void (*progress_gauge_callback)
1255 (CtdlIPC*, unsigned long, unsigned long),
1264 if (!cret) return -1;
1265 if (!buf) return -1;
1266 if (*buf) return -1;
1267 if (!filename) return -1;
1268 if (ipc->downloading) return -1;
1270 aaa = (char *)malloc(strlen(filename) + 6);
1271 if (!aaa) return -1;
1273 sprintf(aaa, "OIMG %s", filename);
1274 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1276 if (ret / 100 == 2) {
1277 ipc->downloading = 1;
1278 bytes = extract_long(cret, 0);
1279 last_mod = extract_int(cret, 1);
1280 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1281 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1282 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1283 ret = CtdlIPCEndDownload(ipc, cret);
1285 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1286 filename, mimetype);
1293 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1295 void (*progress_gauge_callback)
1296 (CtdlIPC*, unsigned long, unsigned long),
1302 if (!cret) return -1;
1303 if (!save_as) return -1;
1304 if (!comment) return -1;
1305 if (!path) return -1;
1306 if (!*path) return -1;
1307 if (ipc->uploading) return -1;
1309 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1310 if (!aaa) return -1;
1312 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1313 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1315 if (ret / 100 == 2) {
1317 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1318 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1326 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1327 const char *save_as,
1328 void (*progress_gauge_callback)
1329 (CtdlIPC*, unsigned long, unsigned long),
1335 if (!cret) return -1;
1336 if (!save_as) return -1;
1337 if (!path && for_real) return -1;
1338 if (!*path && for_real) return -1;
1339 if (ipc->uploading) return -1;
1341 aaa = (char *)malloc(strlen(save_as) + 17);
1342 if (!aaa) return -1;
1344 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1345 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1347 if (ret / 100 == 2 && for_real) {
1349 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1350 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1358 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1363 if (!cret) return -2;
1364 if (!username) return -2;
1366 aaa = (char *)malloc(strlen(username) + 6);
1367 if (!aaa) return -1;
1369 sprintf(aaa, "QUSR %s", username);
1370 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1377 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1381 if (!cret) return -2;
1382 if (!listing) return -2;
1383 if (*listing) return -2;
1385 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1390 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1395 if (!cret) return -2;
1396 if (!name) return -2;
1398 sprintf(aaa, "CFLR %s|%d", name, for_real);
1399 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1405 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1409 if (!cret) return -1;
1410 if (floornum < 0) return -1;
1412 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1413 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1418 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1423 if (!cret) return -2;
1424 if (!floorname) return -2;
1425 if (floornum < 0) return -2;
1427 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1428 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1436 * You only need to fill out hostname, the defaults will be used if any of the
1437 * other fields are not set properly.
1439 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1440 int revision, const char *software_name, const char *hostname,
1446 if (developerid < 0 || clientid < 0 || revision < 0 ||
1450 revision = REV_LEVEL - 600;
1451 software_name = "Citadel (libcitadel)";
1453 if (!hostname) return -2;
1455 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1456 if (!aaa) return -1;
1458 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1459 revision, software_name, hostname);
1460 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1467 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1473 if (!cret) return -2;
1474 if (!username) return -2;
1476 aaa = (char *)malloc(strlen(username) + 8);
1477 if (!aaa) return -1;
1480 sprintf(aaa, "SEXP %s|-", username);
1481 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1484 sprintf(aaa, "SEXP %s||", username);
1485 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1493 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1497 if (!cret) return -2;
1498 if (!listing) return -2;
1499 if (*listing) return -2;
1501 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1506 /* mode is 0 = enable, 1 = disable, 2 = status */
1507 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1511 if (!cret) return -2;
1513 sprintf(aaa, "DEXP %d", mode);
1514 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1519 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1521 if (!cret) return -2;
1522 if (!bio) return -2;
1524 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1530 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1536 if (!cret) return -2;
1537 if (!username) return -2;
1538 if (!listing) return -2;
1539 if (*listing) return -2;
1541 aaa = (char *)malloc(strlen(username) + 6);
1542 if (!aaa) return -1;
1544 sprintf(aaa, "RBIO %s", username);
1545 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1552 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1556 if (!cret) return -2;
1557 if (!listing) return -2;
1558 if (*listing) return -2;
1560 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1565 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1569 if (!cret) return -1;
1571 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1572 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1577 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1581 if (!cret) return -1;
1583 sprintf(aaa, "TERM %d", sid);
1584 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1589 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1591 if (!cret) return -1;
1593 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1598 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1602 if (!cret) return -1;
1604 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1605 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1610 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1616 if (!cret) return -2;
1617 if (!text) return -2;
1618 if (!filename) return -2;
1620 aaa = (char *)malloc(strlen(filename) + 6);
1621 if (!aaa) return -1;
1623 sprintf(aaa, "EMSG %s", filename);
1624 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1631 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1636 if (!cret) return -2;
1637 if (!hostname) return -2;
1639 aaa = (char *)malloc(strlen(hostname) + 6);
1640 if (!aaa) return -1;
1642 sprintf(aaa, "HCHG %s", hostname);
1643 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1650 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1655 if (!cret) return -2;
1656 if (!roomname) return -2;
1658 aaa = (char *)malloc(strlen(roomname) + 6);
1659 if (!aaa) return -1;
1661 sprintf(aaa, "RCHG %s", roomname);
1662 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1669 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1674 if (!cret) return -2;
1675 if (!username) return -2;
1677 aaa = (char *)malloc(strlen(username) + 6);
1678 if (!aaa) return -1;
1680 sprintf(aaa, "UCHG %s", username);
1681 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1688 /* This function returns the actual server time reported, or 0 if error */
1689 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1691 register time_t tret;
1694 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1695 if (ret / 100 == 2) {
1696 tret = extract_long(cret, 0);
1705 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1706 struct ctdluser **uret, char *cret)
1711 if (!cret) return -2;
1712 if (!uret) return -2;
1713 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1714 if (!*uret) return -1;
1716 sprintf(aaa, "AGUP %s", who);
1717 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1719 if (ret / 100 == 2) {
1720 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1721 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1722 uret[0]->flags = extract_int(cret, 2);
1723 uret[0]->timescalled = extract_long(cret, 3);
1724 uret[0]->posted = extract_long(cret, 4);
1725 uret[0]->axlevel = extract_int(cret, 5);
1726 uret[0]->usernum = extract_long(cret, 6);
1727 uret[0]->lastcall = extract_long(cret, 7);
1728 uret[0]->USuserpurge = extract_int(cret, 8);
1735 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1740 if (!cret) return -2;
1741 if (!uret) return -2;
1743 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1744 if (!aaa) return -1;
1746 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1747 uret->fullname, uret->password, uret->flags,
1748 uret->timescalled, uret->posted, uret->axlevel,
1749 uret->usernum, uret->lastcall, uret->USuserpurge);
1750 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1757 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1758 /* caller must free the struct ExpirePolicy */
1759 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1760 struct ExpirePolicy **policy, char *cret)
1762 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1766 if (!cret) return -2;
1767 if (!policy) return -2;
1768 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1769 if (!*policy) return -1;
1770 if (which < 0 || which > 3) return -2;
1772 sprintf(aaa, "GPEX %s", proto[which]);
1773 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1774 if (ret / 100 == 2) {
1775 policy[0]->expire_mode = extract_int(cret, 0);
1776 policy[0]->expire_value = extract_int(cret, 1);
1784 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1785 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1786 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1787 struct ExpirePolicy *policy, char *cret)
1790 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1792 if (!cret) return -2;
1793 if (which < 0 || which > 3) return -2;
1794 if (!policy) return -2;
1795 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1796 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1798 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1799 policy->expire_mode, policy->expire_value);
1800 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1805 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1809 if (!cret) return -2;
1810 if (!listing) return -2;
1811 if (*listing) return -2;
1813 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1814 listing, &bytes, cret);
1819 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1821 if (!cret) return -2;
1822 if (!listing) return -2;
1824 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1830 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1831 char **listing, char *cret)
1836 if (!cret) return -2;
1837 if (!mimetype) return -2;
1838 if (!listing) return -2;
1839 if (*listing) return -2;
1841 aaa = malloc(strlen(mimetype) + 13);
1842 if (!aaa) return -1;
1843 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1844 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1845 listing, &bytes, cret);
1850 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1851 const char *listing, char *cret)
1855 if (!cret) return -2;
1856 if (!mimetype) return -2;
1857 if (!listing) return -2;
1859 aaa = malloc(strlen(mimetype) + 13);
1860 if (!aaa) return -1;
1861 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1862 return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1868 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1872 if (!cret) return -2;
1873 if (!listing) return -2;
1874 if (*listing) return -2;
1876 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1877 listing, &bytes, cret);
1882 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1884 if (!cret) return -2;
1885 if (!listing) return -2;
1887 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1893 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1897 if (!cret) return -2;
1898 if (session < 0) return -2;
1900 sprintf(aaa, "REQT %d", session);
1901 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1906 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1910 if (!cret) return -2;
1911 if (msgnum < 0) return -2;
1913 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1914 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1919 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1928 /* New SSL object */
1929 temp_ssl = SSL_new(ssl_ctx);
1931 error_printf("SSL_new failed: %s\n",
1932 ERR_reason_error_string(ERR_get_error()));
1935 /* Pointless flag waving */
1936 #if SSLEAY_VERSION_NUMBER >= 0x0922
1937 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1940 if (!access(EGD_POOL, F_OK))
1943 if (!RAND_status()) {
1944 error_printf("PRNG not properly seeded\n");
1948 /* Associate network connection with SSL object */
1949 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1950 error_printf("SSL_set_fd failed: %s\n",
1951 ERR_reason_error_string(ERR_get_error()));
1955 if (status_hook != NULL)
1956 status_hook("Requesting encryption...\r");
1958 /* Ready to start SSL/TLS */
1960 CtdlIPC_putline(ipc, "STLS");
1961 CtdlIPC_getline(ipc, buf);
1962 if (buf[0] != '2') {
1963 error_printf("Server can't start TLS: %s\n", buf);
1967 r = CtdlIPCGenericCommand(ipc,
1968 "STLS", NULL, 0, NULL, NULL, cret);
1970 error_printf("Server can't start TLS: %s\n", buf);
1975 /* Do SSL/TLS handshake */
1976 if ((a = SSL_connect(temp_ssl)) < 1) {
1977 error_printf("SSL_connect failed: %s\n",
1978 ERR_reason_error_string(ERR_get_error()));
1982 ipc->ssl = temp_ssl;
1984 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1988 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1989 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1990 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1991 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1997 #endif /* HAVE_OPENSSL */
2002 static void endtls(SSL *ssl)
2013 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2017 if (!address) return -2;
2018 if (!cret) return -2;
2020 aaa = (char *)malloc(strlen(address) + 6);
2021 if (!aaa) return -1;
2023 sprintf(aaa, "QDIR %s", address);
2024 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2029 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2033 if (!cret) return -2;
2034 sprintf(aaa, "IPGM %d", secret);
2035 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2040 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2044 if (!cret) return -2;
2045 if (!mret) return -2;
2046 if (*mret) return -2;
2048 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2069 /* ************************************************************************** */
2070 /* Stuff below this line is not for public consumption */
2071 /* ************************************************************************** */
2074 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2076 if (ipc->network_status_cb) ipc->network_status_cb(1);
2077 #ifdef THREADED_CLIENT
2078 pthread_mutex_lock(&(ipc->mutex));
2083 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2085 #ifdef THREADED_CLIENT
2086 pthread_mutex_unlock(&(ipc->mutex));
2088 if (ipc->network_status_cb) ipc->network_status_cb(0);
2092 /* Read a listing from the server up to 000. Append to dest if it exists */
2093 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2102 length = strlen(ret);
2107 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2108 linelength = strlen(aaa);
2109 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2111 strcpy(&ret[length], aaa);
2112 length += linelength;
2113 strcpy(&ret[length++], "\n");
2121 /* Send a listing to the server; generate the ending 000. */
2122 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2126 text = (char *)malloc(strlen(listing) + 6);
2128 strcpy(text, listing);
2129 while (text[strlen(text) - 1] == '\n')
2130 text[strlen(text) - 1] = '\0';
2131 strcat(text, "\n000");
2132 CtdlIPC_putline(ipc, text);
2136 /* Malloc failed but we are committed to send */
2137 /* This may result in extra blanks at the bottom */
2138 CtdlIPC_putline(ipc, text);
2139 CtdlIPC_putline(ipc, "000");
2145 /* Partial read of file from server */
2146 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2148 register size_t len = 0;
2152 if (!cret) return 0;
2153 if (bytes < 1) return 0;
2156 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2157 CtdlIPC_putline(ipc, aaa);
2158 CtdlIPC_getline(ipc, aaa);
2160 strcpy(cret, &aaa[4]);
2162 len = extract_long(&aaa[4], 0);
2163 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2165 /* I know what I'm doing */
2166 serv_read(ipc, ((char *)(*buf) + offset), len);
2168 /* We have to read regardless */
2169 serv_read(ipc, aaa, len);
2173 CtdlIPC_unlock(ipc);
2179 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2183 if (!cret) return -2;
2184 if (!ipc->downloading) return -2;
2186 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2188 ipc->downloading = 0;
2194 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2198 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2199 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2206 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2207 void (*progress_gauge_callback)
2208 (CtdlIPC*, unsigned long, unsigned long),
2211 register size_t len;
2213 if (!cret) return -1;
2214 if (!buf) return -1;
2215 if (*buf) return -1;
2216 if (!ipc->downloading) return -1;
2219 if (progress_gauge_callback)
2220 progress_gauge_callback(ipc, len, bytes);
2221 while (len < bytes) {
2222 register size_t block;
2224 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2230 if (progress_gauge_callback)
2231 progress_gauge_callback(ipc, len, bytes);
2236 /* READ - pipelined */
2237 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2239 void (*progress_gauge_callback)
2240 (CtdlIPC*, unsigned long, unsigned long),
2243 register size_t len;
2244 register int calls; /* How many calls in the pipeline */
2245 register int i; /* iterator */
2248 if (!cret) return -1;
2249 if (!buf) return -1;
2250 if (*buf) return -1;
2251 if (!ipc->downloading) return -1;
2253 *buf = (void *)realloc(*buf, bytes - resume);
2254 if (!*buf) return -1;
2258 if (progress_gauge_callback)
2259 progress_gauge_callback(ipc, len, bytes);
2261 /* How many calls will be in the pipeline? */
2262 calls = (bytes - resume) / 4096;
2263 if ((bytes - resume) % 4096) calls++;
2265 /* Send all requests at once */
2266 for (i = 0; i < calls; i++) {
2267 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2268 CtdlIPC_putline(ipc, aaa);
2271 /* Receive all responses at once */
2272 for (i = 0; i < calls; i++) {
2273 CtdlIPC_getline(ipc, aaa);
2275 strcpy(cret, &aaa[4]);
2277 len = extract_long(&aaa[4], 0);
2278 /* I know what I'm doing */
2279 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2281 if (progress_gauge_callback)
2282 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2284 CtdlIPC_unlock(ipc);
2290 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2295 if (!cret) return -1;
2296 if (!ipc->uploading) return -1;
2298 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2299 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2306 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2307 void (*progress_gauge_callback)
2308 (CtdlIPC*, unsigned long, unsigned long),
2311 register int ret = -1;
2312 register size_t offset = 0;
2318 if (!cret) return -1;
2319 if (!path) return -1;
2320 if (!*path) return -1;
2322 fd = fopen(path, "r");
2325 fseek(fd, 0L, SEEK_END);
2329 if (progress_gauge_callback)
2330 progress_gauge_callback(ipc, 0, bytes);
2332 while (offset < bytes) {
2333 register size_t to_write;
2335 /* Read some data in */
2336 to_write = fread(buf, 1, 4096, fd);
2338 if (feof(fd) || ferror(fd)) break;
2340 sprintf(aaa, "WRIT %d", (int)to_write);
2341 CtdlIPC_putline(ipc, aaa);
2342 CtdlIPC_getline(ipc, aaa);
2343 strcpy(cret, &aaa[4]);
2345 if (aaa[0] == '7') {
2346 to_write = extract_long(&aaa[4], 0);
2348 serv_write(ipc, buf, to_write);
2350 if (progress_gauge_callback)
2351 progress_gauge_callback(ipc, offset, bytes);
2352 /* Detect short reads and back up if needed */
2353 /* offset will never be negative anyway */
2354 fseek(fd, (signed)offset, SEEK_SET);
2359 if (progress_gauge_callback)
2360 progress_gauge_callback(ipc, 1, 1);
2361 return (!ferror(fd) ? ret : -2);
2366 * Generic command method. This method should handle any server command
2367 * except for CHAT. It takes the following arguments:
2369 * ipc The server to speak with
2370 * command Preformatted command to send to server
2371 * to_send A text or binary file to send to server
2372 * (only sent if server requests it)
2373 * bytes_to_send The number of bytes in to_send (required if
2374 * sending binary, optional if sending listing)
2375 * to_receive Pointer to a NULL pointer, if the server
2376 * sends text or binary we will allocate memory
2377 * for the file and stuff it here
2378 * bytes_to_receive If a file is received, we will store its
2380 * proto_response The protocol response. Caller must provide
2381 * this buffer and ensure that it is at least
2382 * 128 bytes in length.
2384 * This function returns a number equal to the protocol response number,
2385 * -1 if an internal error occurred, -2 if caller provided bad values,
2386 * or 0 - the protocol response number if bad values were found during
2387 * the protocol exchange.
2388 * It stores the protocol response string (minus the number) in
2389 * protocol_response as described above. Some commands send additional
2390 * data in this string.
2392 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2393 const char *command, const char *to_send,
2394 size_t bytes_to_send, char **to_receive,
2395 size_t *bytes_to_receive, char *proto_response)
2401 if (!command) return -2;
2402 if (!proto_response) return -2;
2405 if (ipc->ssl) watch_ssl = 1;
2409 CtdlIPC_putline(ipc, command);
2411 CtdlIPC_getline(ipc, proto_response);
2412 if (proto_response[3] == '*')
2414 ret = atoi(proto_response);
2415 strcpy(proto_response, &proto_response[4]);
2416 switch (ret / 100) {
2417 default: /* Unknown, punt */
2419 case 3: /* MORE_DATA */
2421 /* Don't need to do anything */
2423 case 1: /* LISTING_FOLLOWS */
2424 if (to_receive && !*to_receive && bytes_to_receive) {
2425 *to_receive = CtdlIPCReadListing(ipc, NULL);
2426 } else { /* Drain */
2427 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2431 case 4: /* SEND_LISTING */
2433 CtdlIPCSendListing(ipc, to_send);
2435 /* No listing given, fake it */
2436 CtdlIPC_putline(ipc, "000");
2440 case 6: /* BINARY_FOLLOWS */
2441 if (to_receive && !*to_receive && bytes_to_receive) {
2443 extract_long(proto_response, 0);
2444 *to_receive = (char *)
2445 malloc((size_t)*bytes_to_receive);
2449 serv_read(ipc, *to_receive,
2456 drain = extract_long(proto_response, 0);
2457 while (drain > SIZ) {
2458 serv_read(ipc, buf, SIZ);
2461 serv_read(ipc, buf, drain);
2465 case 7: /* SEND_BINARY */
2466 if (to_send && bytes_to_send) {
2467 serv_write(ipc, to_send, bytes_to_send);
2468 } else if (bytes_to_send) {
2469 /* Fake it, send nulls */
2472 fake = bytes_to_send;
2473 memset(buf, '\0', SIZ);
2474 while (fake > SIZ) {
2475 serv_write(ipc, buf, SIZ);
2478 serv_write(ipc, buf, fake);
2480 } /* else who knows? DANGER WILL ROBINSON */
2482 case 8: /* START_CHAT_MODE */
2483 if (!strncasecmp(command, "CHAT", 4)) {
2484 /* Don't call chatmode with generic! */
2485 CtdlIPC_putline(ipc, "/quit");
2488 /* In this mode we send then receive listing */
2490 CtdlIPCSendListing(ipc, to_send);
2492 /* No listing given, fake it */
2493 CtdlIPC_putline(ipc, "000");
2496 if (to_receive && !*to_receive
2497 && bytes_to_receive) {
2498 *to_receive = CtdlIPCReadListing(ipc, NULL);
2499 } else { /* Drain */
2500 while (CtdlIPC_getline(ipc, buf),
2501 strcmp(buf, "000")) ;
2506 case 9: /* ASYNC_MSG */
2507 /* CtdlIPCDoAsync(ret, proto_response); */
2508 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2514 CtdlIPC_unlock(ipc);
2519 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2521 struct hostent *phe;
2522 struct servent *pse;
2523 struct protoent *ppe;
2524 struct sockaddr_in sin;
2527 memset(&sin, 0, sizeof(sin));
2528 sin.sin_family = AF_INET;
2530 pse = getservbyname(service, protocol);
2532 sin.sin_port = pse->s_port;
2534 else if (atoi(service) > 0) {
2535 sin.sin_port = htons(atoi(service));
2538 sin.sin_port = htons(defaultPort);
2540 phe = gethostbyname(host);
2542 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2543 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2546 if ((ppe = getprotobyname(protocol)) == 0) {
2549 if (!strcmp(protocol, "udp")) {
2555 s = socket(PF_INET, type, ppe->p_proto);
2560 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2568 static int uds_connectsock(int *isLocal, char *sockpath)
2570 struct sockaddr_un addr;
2573 memset(&addr, 0, sizeof(addr));
2574 addr.sun_family = AF_UNIX;
2575 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2577 s = socket(AF_UNIX, SOCK_STREAM, 0);
2582 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2593 * input binary data from socket
2595 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2597 unsigned int len, rlen;
2599 #if defined(HAVE_OPENSSL)
2601 serv_read_ssl(ipc, buf, bytes);
2606 while (len < bytes) {
2607 rlen = read(ipc->sock, &buf[len], bytes - len);
2609 connection_died(ipc, 0);
2618 * send binary to server
2620 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2622 unsigned int bytes_written = 0;
2625 #if defined(HAVE_OPENSSL)
2627 serv_write_ssl(ipc, buf, nbytes);
2631 while (bytes_written < nbytes) {
2632 retval = write(ipc->sock, &buf[bytes_written],
2633 nbytes - bytes_written);
2635 connection_died(ipc, 0);
2638 bytes_written += retval;
2645 * input binary data from encrypted connection
2647 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2653 while (len < bytes) {
2654 if (SSL_want_read(ipc->ssl)) {
2655 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2656 error_printf("SSL_write in serv_read:\n");
2657 ERR_print_errors_fp(stderr);
2660 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2664 errval = SSL_get_error(ipc->ssl, rlen);
2665 if (errval == SSL_ERROR_WANT_READ ||
2666 errval == SSL_ERROR_WANT_WRITE) {
2671 Not sure why we'd want to handle these error codes any differently,
2672 but this definitely isn't the way to handle them. Someone must have
2673 naively assumed that we could fall back to unencrypted communications,
2674 but all it does is just recursively blow the stack.
2675 if (errval == SSL_ERROR_ZERO_RETURN ||
2676 errval == SSL_ERROR_SSL) {
2677 serv_read(ipc, &buf[len], bytes - len);
2681 error_printf("SSL_read in serv_read: %s\n",
2682 ERR_reason_error_string(ERR_peek_error()));
2683 connection_died(ipc, 1);
2692 * send binary to server encrypted
2694 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2696 unsigned int bytes_written = 0;
2700 while (bytes_written < nbytes) {
2701 if (SSL_want_write(ipc->ssl)) {
2702 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2703 error_printf("SSL_read in serv_write:\n");
2704 ERR_print_errors_fp(stderr);
2707 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2708 nbytes - bytes_written);
2712 errval = SSL_get_error(ipc->ssl, retval);
2713 if (errval == SSL_ERROR_WANT_READ ||
2714 errval == SSL_ERROR_WANT_WRITE) {
2718 if (errval == SSL_ERROR_ZERO_RETURN ||
2719 errval == SSL_ERROR_SSL) {
2720 serv_write(ipc, &buf[bytes_written],
2721 nbytes - bytes_written);
2724 error_printf("SSL_write in serv_write: %s\n",
2725 ERR_reason_error_string(ERR_peek_error()));
2726 connection_died(ipc, 1);
2729 bytes_written += retval;
2734 static void CtdlIPC_init_OpenSSL(void)
2737 SSL_METHOD *ssl_method;
2740 /* already done init */
2749 SSL_load_error_strings();
2750 SSLeay_add_ssl_algorithms();
2752 /* Set up the SSL context in which we will oeprate */
2753 ssl_method = SSLv23_client_method();
2754 ssl_ctx = SSL_CTX_new(ssl_method);
2756 error_printf("SSL_CTX_new failed: %s\n",
2757 ERR_reason_error_string(ERR_get_error()));
2760 /* Any reasonable cipher we can get */
2761 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2762 error_printf("No ciphers available for encryption\n");
2765 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2767 /* Load DH parameters into the context */
2770 error_printf("Can't allocate a DH object: %s\n",
2771 ERR_reason_error_string(ERR_get_error()));
2774 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2775 error_printf("Can't assign DH_P: %s\n",
2776 ERR_reason_error_string(ERR_get_error()));
2780 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2781 error_printf("Can't assign DH_G: %s\n",
2782 ERR_reason_error_string(ERR_get_error()));
2787 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2790 #ifdef THREADED_CLIENT
2791 /* OpenSSL requires callbacks for threaded clients */
2792 CRYPTO_set_locking_callback(ssl_lock);
2793 CRYPTO_set_id_callback(id_callback);
2795 /* OpenSSL requires us to do semaphores for threaded clients */
2796 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2798 perror("malloc failed");
2801 for (a = 0; a < CRYPTO_num_locks(); a++) {
2802 Critters[a] = malloc(sizeof (pthread_mutex_t));
2804 perror("malloc failed");
2807 pthread_mutex_init(Critters[a], NULL);
2810 #endif /* THREADED_CLIENT */
2814 static void ssl_lock(int mode, int n, const char *file, int line)
2816 #ifdef THREADED_CLIENT
2817 if (mode & CRYPTO_LOCK)
2818 pthread_mutex_lock(Critters[n]);
2820 pthread_mutex_unlock(Critters[n]);
2821 #endif /* THREADED_CLIENT */
2824 #ifdef THREADED_CLIENT
2825 static unsigned long id_callback(void) {
2826 return (unsigned long)pthread_self();
2828 #endif /* THREADED_CLIENT */
2829 #endif /* HAVE_OPENSSL */
2833 * input string from socket - implemented in terms of serv_read()
2835 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2839 /* Read one character at a time. */
2841 serv_read(ipc, &buf[i], 1);
2842 if (buf[i] == '\n' || i == (SIZ-1))
2846 /* If we got a long line, discard characters until the newline. */
2848 while (buf[i] != '\n')
2849 serv_read(ipc, &buf[i], 1);
2851 /* Strip the trailing newline (and carriage return, if present) */
2852 if (buf[i] == 10) buf[i--] = 0;
2853 if (buf[i] == 13) buf[i--] = 0;
2856 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2858 CtdlIPC_getline(ipc, buf);
2862 * send line to server - implemented in terms of serv_write()
2864 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2870 cmd = malloc(len + 2);
2872 /* This requires no extra memory */
2873 serv_write(ipc, buf, len);
2874 serv_write(ipc, "\n", 1);
2876 /* This is network-optimized */
2877 strncpy(cmd, buf, len);
2878 strcpy(cmd + len, "\n");
2879 serv_write(ipc, cmd, len + 1);
2883 ipc->last_command_sent = time(NULL);
2886 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2888 CtdlIPC_putline(ipc, buf);
2895 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2903 ipc = ialloc(CtdlIPC);
2907 #if defined(HAVE_OPENSSL)
2909 CtdlIPC_init_OpenSSL();
2911 #if defined(HAVE_PTHREAD_H)
2912 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2914 ipc->sock = -1; /* Not connected */
2915 ipc->isLocal = 0; /* Not local, of course! */
2916 ipc->downloading = 0;
2918 ipc->last_command_sent = 0L;
2919 ipc->network_status_cb = NULL;
2921 strcpy(cithost, DEFAULT_HOST); /* default host */
2922 strcpy(citport, DEFAULT_PORT); /* default port */
2924 /* Allow caller to supply our values (Windows) */
2925 if (hostbuf && strlen(hostbuf) > 0)
2926 strcpy(cithost, hostbuf);
2927 if (portbuf && strlen(portbuf) > 0)
2928 strcpy(citport, portbuf);
2930 /* Read host/port from command line if present */
2931 for (a = 0; a < argc; ++a) {
2934 } else if (a == 1) {
2935 strcpy(cithost, argv[a]);
2936 } else if (a == 2) {
2937 strcpy(citport, argv[a]);
2939 error_printf("%s: usage: ",argv[0]);
2940 error_printf("%s [host] [port] ",argv[0]);
2947 if ((!strcmp(cithost, "localhost"))
2948 || (!strcmp(cithost, "127.0.0.1"))) {
2952 /* If we're using a unix domain socket we can do a bunch of stuff */
2953 if (!strcmp(cithost, UDS)) {
2954 if (!strcasecmp(citport, DEFAULT_PORT)) {
2955 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2958 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2960 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2961 if (ipc->sock == -1) {
2965 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2966 if (portbuf != NULL) strcpy(portbuf, sockpath);
2970 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2971 if (ipc->sock == -1) {
2975 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2976 if (portbuf != NULL) strcpy(portbuf, citport);
2982 * Disconnect and delete the IPC class (destructor)
2984 void CtdlIPC_delete(CtdlIPC* ipc)
2988 SSL_shutdown(ipc->ssl);
2993 if (ipc->sock > -1) {
2994 shutdown(ipc->sock, 2); /* Close it up */
3002 * Disconnect and delete the IPC class (destructor)
3003 * Also NULLs out the pointer
3005 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3007 CtdlIPC_delete(*pipc);
3013 * return the file descriptor of the server socket so we can select() on it.
3015 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3018 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3025 * return one character
3027 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3030 char CtdlIPC_get(CtdlIPC* ipc)
3035 serv_read(ipc, buf, 1);