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(cmd, "GPEX %s", proto[which]);
1773 ret = CtdlIPCGenericCommand(ipc, cmd, 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);
1783 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1784 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1785 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1786 struct ExpirePolicy *policy, char *cret)
1789 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1791 if (!cret) return -2;
1792 if (which < 0 || which > 3) return -2;
1793 if (!policy) return -2;
1794 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1795 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1797 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1798 policy->expire_mode, policy->expire_value);
1799 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1804 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1808 if (!cret) return -2;
1809 if (!listing) return -2;
1810 if (*listing) return -2;
1812 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1813 listing, &bytes, cret);
1818 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1820 if (!cret) return -2;
1821 if (!listing) return -2;
1823 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1829 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1830 char **listing, char *cret)
1835 if (!cret) return -2;
1836 if (!mimetype) return -2;
1837 if (!listing) return -2;
1838 if (*listing) return -2;
1840 aaa = malloc(strlen(mimetype) + 13);
1841 if (!aaa) return -1;
1842 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1843 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1844 listing, &bytes, cret);
1849 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1850 const char *listing, char *cret)
1854 if (!cret) return -2;
1855 if (!mimetype) return -2;
1856 if (!listing) return -2;
1858 aaa = malloc(strlen(mimetype) + 13);
1859 if (!aaa) return -1;
1860 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1861 return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1867 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1871 if (!cret) return -2;
1872 if (!listing) return -2;
1873 if (*listing) return -2;
1875 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1876 listing, &bytes, cret);
1881 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1883 if (!cret) return -2;
1884 if (!listing) return -2;
1886 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1892 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1896 if (!cret) return -2;
1897 if (session < 0) return -2;
1899 sprintf(aaa, "REQT %d", session);
1900 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1905 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1909 if (!cret) return -2;
1910 if (msgnum < 0) return -2;
1912 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1913 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1918 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1927 /* New SSL object */
1928 temp_ssl = SSL_new(ssl_ctx);
1930 error_printf("SSL_new failed: %s\n",
1931 ERR_reason_error_string(ERR_get_error()));
1934 /* Pointless flag waving */
1935 #if SSLEAY_VERSION_NUMBER >= 0x0922
1936 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1939 if (!access(EGD_POOL, F_OK))
1942 if (!RAND_status()) {
1943 error_printf("PRNG not properly seeded\n");
1947 /* Associate network connection with SSL object */
1948 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1949 error_printf("SSL_set_fd failed: %s\n",
1950 ERR_reason_error_string(ERR_get_error()));
1954 if (status_hook != NULL)
1955 status_hook("Requesting encryption...\r");
1957 /* Ready to start SSL/TLS */
1959 CtdlIPC_putline(ipc, "STLS");
1960 CtdlIPC_getline(ipc, buf);
1961 if (buf[0] != '2') {
1962 error_printf("Server can't start TLS: %s\n", buf);
1966 r = CtdlIPCGenericCommand(ipc,
1967 "STLS", NULL, 0, NULL, NULL, cret);
1969 error_printf("Server can't start TLS: %s\n", buf);
1974 /* Do SSL/TLS handshake */
1975 if ((a = SSL_connect(temp_ssl)) < 1) {
1976 error_printf("SSL_connect failed: %s\n",
1977 ERR_reason_error_string(ERR_get_error()));
1981 ipc->ssl = temp_ssl;
1983 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1987 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1988 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1989 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1990 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1996 #endif /* HAVE_OPENSSL */
2001 static void endtls(SSL *ssl)
2012 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2016 if (!address) return -2;
2017 if (!cret) return -2;
2019 aaa = (char *)malloc(strlen(address) + 6);
2020 if (!aaa) return -1;
2022 sprintf(aaa, "QDIR %s", address);
2023 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2028 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2032 if (!cret) return -2;
2033 sprintf(aaa, "IPGM %d", secret);
2034 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2039 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2043 if (!cret) return -2;
2044 if (!mret) return -2;
2045 if (*mret) return -2;
2047 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2068 /* ************************************************************************** */
2069 /* Stuff below this line is not for public consumption */
2070 /* ************************************************************************** */
2073 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2075 if (ipc->network_status_cb) ipc->network_status_cb(1);
2076 #ifdef THREADED_CLIENT
2077 pthread_mutex_lock(&(ipc->mutex));
2082 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2084 #ifdef THREADED_CLIENT
2085 pthread_mutex_unlock(&(ipc->mutex));
2087 if (ipc->network_status_cb) ipc->network_status_cb(0);
2091 /* Read a listing from the server up to 000. Append to dest if it exists */
2092 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2101 length = strlen(ret);
2106 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2107 linelength = strlen(aaa);
2108 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2110 strcpy(&ret[length], aaa);
2111 length += linelength;
2112 strcpy(&ret[length++], "\n");
2120 /* Send a listing to the server; generate the ending 000. */
2121 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2125 text = (char *)malloc(strlen(listing) + 6);
2127 strcpy(text, listing);
2128 while (text[strlen(text) - 1] == '\n')
2129 text[strlen(text) - 1] = '\0';
2130 strcat(text, "\n000");
2131 CtdlIPC_putline(ipc, text);
2135 /* Malloc failed but we are committed to send */
2136 /* This may result in extra blanks at the bottom */
2137 CtdlIPC_putline(ipc, text);
2138 CtdlIPC_putline(ipc, "000");
2144 /* Partial read of file from server */
2145 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2147 register size_t len = 0;
2151 if (!cret) return 0;
2152 if (bytes < 1) return 0;
2155 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2156 CtdlIPC_putline(ipc, aaa);
2157 CtdlIPC_getline(ipc, aaa);
2159 strcpy(cret, &aaa[4]);
2161 len = extract_long(&aaa[4], 0);
2162 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2164 /* I know what I'm doing */
2165 serv_read(ipc, ((char *)(*buf) + offset), len);
2167 /* We have to read regardless */
2168 serv_read(ipc, aaa, len);
2172 CtdlIPC_unlock(ipc);
2178 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2182 if (!cret) return -2;
2183 if (!ipc->downloading) return -2;
2185 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2187 ipc->downloading = 0;
2193 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2197 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2198 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2205 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2206 void (*progress_gauge_callback)
2207 (CtdlIPC*, unsigned long, unsigned long),
2210 register size_t len;
2212 if (!cret) return -1;
2213 if (!buf) return -1;
2214 if (*buf) return -1;
2215 if (!ipc->downloading) return -1;
2218 if (progress_gauge_callback)
2219 progress_gauge_callback(ipc, len, bytes);
2220 while (len < bytes) {
2221 register size_t block;
2223 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2229 if (progress_gauge_callback)
2230 progress_gauge_callback(ipc, len, bytes);
2235 /* READ - pipelined */
2236 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2238 void (*progress_gauge_callback)
2239 (CtdlIPC*, unsigned long, unsigned long),
2242 register size_t len;
2243 register int calls; /* How many calls in the pipeline */
2244 register int i; /* iterator */
2247 if (!cret) return -1;
2248 if (!buf) return -1;
2249 if (*buf) return -1;
2250 if (!ipc->downloading) return -1;
2252 *buf = (void *)realloc(*buf, bytes - resume);
2253 if (!*buf) return -1;
2257 if (progress_gauge_callback)
2258 progress_gauge_callback(ipc, len, bytes);
2260 /* How many calls will be in the pipeline? */
2261 calls = (bytes - resume) / 4096;
2262 if ((bytes - resume) % 4096) calls++;
2264 /* Send all requests at once */
2265 for (i = 0; i < calls; i++) {
2266 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2267 CtdlIPC_putline(ipc, aaa);
2270 /* Receive all responses at once */
2271 for (i = 0; i < calls; i++) {
2272 CtdlIPC_getline(ipc, aaa);
2274 strcpy(cret, &aaa[4]);
2276 len = extract_long(&aaa[4], 0);
2277 /* I know what I'm doing */
2278 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2280 if (progress_gauge_callback)
2281 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2283 CtdlIPC_unlock(ipc);
2289 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2294 if (!cret) return -1;
2295 if (!ipc->uploading) return -1;
2297 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2298 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2305 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2306 void (*progress_gauge_callback)
2307 (CtdlIPC*, unsigned long, unsigned long),
2310 register int ret = -1;
2311 register size_t offset = 0;
2317 if (!cret) return -1;
2318 if (!path) return -1;
2319 if (!*path) return -1;
2321 fd = fopen(path, "r");
2324 fseek(fd, 0L, SEEK_END);
2328 if (progress_gauge_callback)
2329 progress_gauge_callback(ipc, 0, bytes);
2331 while (offset < bytes) {
2332 register size_t to_write;
2334 /* Read some data in */
2335 to_write = fread(buf, 1, 4096, fd);
2337 if (feof(fd) || ferror(fd)) break;
2339 sprintf(aaa, "WRIT %d", (int)to_write);
2340 CtdlIPC_putline(ipc, aaa);
2341 CtdlIPC_getline(ipc, aaa);
2342 strcpy(cret, &aaa[4]);
2344 if (aaa[0] == '7') {
2345 to_write = extract_long(&aaa[4], 0);
2347 serv_write(ipc, buf, to_write);
2349 if (progress_gauge_callback)
2350 progress_gauge_callback(ipc, offset, bytes);
2351 /* Detect short reads and back up if needed */
2352 /* offset will never be negative anyway */
2353 fseek(fd, (signed)offset, SEEK_SET);
2358 if (progress_gauge_callback)
2359 progress_gauge_callback(ipc, 1, 1);
2360 return (!ferror(fd) ? ret : -2);
2365 * Generic command method. This method should handle any server command
2366 * except for CHAT. It takes the following arguments:
2368 * ipc The server to speak with
2369 * command Preformatted command to send to server
2370 * to_send A text or binary file to send to server
2371 * (only sent if server requests it)
2372 * bytes_to_send The number of bytes in to_send (required if
2373 * sending binary, optional if sending listing)
2374 * to_receive Pointer to a NULL pointer, if the server
2375 * sends text or binary we will allocate memory
2376 * for the file and stuff it here
2377 * bytes_to_receive If a file is received, we will store its
2379 * proto_response The protocol response. Caller must provide
2380 * this buffer and ensure that it is at least
2381 * 128 bytes in length.
2383 * This function returns a number equal to the protocol response number,
2384 * -1 if an internal error occurred, -2 if caller provided bad values,
2385 * or 0 - the protocol response number if bad values were found during
2386 * the protocol exchange.
2387 * It stores the protocol response string (minus the number) in
2388 * protocol_response as described above. Some commands send additional
2389 * data in this string.
2391 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2392 const char *command, const char *to_send,
2393 size_t bytes_to_send, char **to_receive,
2394 size_t *bytes_to_receive, char *proto_response)
2400 if (!command) return -2;
2401 if (!proto_response) return -2;
2404 if (ipc->ssl) watch_ssl = 1;
2408 CtdlIPC_putline(ipc, command);
2410 CtdlIPC_getline(ipc, proto_response);
2411 if (proto_response[3] == '*')
2413 ret = atoi(proto_response);
2414 strcpy(proto_response, &proto_response[4]);
2415 switch (ret / 100) {
2416 default: /* Unknown, punt */
2418 case 3: /* MORE_DATA */
2420 /* Don't need to do anything */
2422 case 1: /* LISTING_FOLLOWS */
2423 if (to_receive && !*to_receive && bytes_to_receive) {
2424 *to_receive = CtdlIPCReadListing(ipc, NULL);
2425 } else { /* Drain */
2426 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2430 case 4: /* SEND_LISTING */
2432 CtdlIPCSendListing(ipc, to_send);
2434 /* No listing given, fake it */
2435 CtdlIPC_putline(ipc, "000");
2439 case 6: /* BINARY_FOLLOWS */
2440 if (to_receive && !*to_receive && bytes_to_receive) {
2442 extract_long(proto_response, 0);
2443 *to_receive = (char *)
2444 malloc((size_t)*bytes_to_receive);
2448 serv_read(ipc, *to_receive,
2455 drain = extract_long(proto_response, 0);
2456 while (drain > SIZ) {
2457 serv_read(ipc, buf, SIZ);
2460 serv_read(ipc, buf, drain);
2464 case 7: /* SEND_BINARY */
2465 if (to_send && bytes_to_send) {
2466 serv_write(ipc, to_send, bytes_to_send);
2467 } else if (bytes_to_send) {
2468 /* Fake it, send nulls */
2471 fake = bytes_to_send;
2472 memset(buf, '\0', SIZ);
2473 while (fake > SIZ) {
2474 serv_write(ipc, buf, SIZ);
2477 serv_write(ipc, buf, fake);
2479 } /* else who knows? DANGER WILL ROBINSON */
2481 case 8: /* START_CHAT_MODE */
2482 if (!strncasecmp(command, "CHAT", 4)) {
2483 /* Don't call chatmode with generic! */
2484 CtdlIPC_putline(ipc, "/quit");
2487 /* In this mode we send then receive listing */
2489 CtdlIPCSendListing(ipc, to_send);
2491 /* No listing given, fake it */
2492 CtdlIPC_putline(ipc, "000");
2495 if (to_receive && !*to_receive
2496 && bytes_to_receive) {
2497 *to_receive = CtdlIPCReadListing(ipc, NULL);
2498 } else { /* Drain */
2499 while (CtdlIPC_getline(ipc, buf),
2500 strcmp(buf, "000")) ;
2505 case 9: /* ASYNC_MSG */
2506 /* CtdlIPCDoAsync(ret, proto_response); */
2507 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2513 CtdlIPC_unlock(ipc);
2518 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2520 struct hostent *phe;
2521 struct servent *pse;
2522 struct protoent *ppe;
2523 struct sockaddr_in sin;
2526 memset(&sin, 0, sizeof(sin));
2527 sin.sin_family = AF_INET;
2529 pse = getservbyname(service, protocol);
2531 sin.sin_port = pse->s_port;
2533 else if (atoi(service) > 0) {
2534 sin.sin_port = htons(atoi(service));
2537 sin.sin_port = htons(defaultPort);
2539 phe = gethostbyname(host);
2541 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2542 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2545 if ((ppe = getprotobyname(protocol)) == 0) {
2548 if (!strcmp(protocol, "udp")) {
2554 s = socket(PF_INET, type, ppe->p_proto);
2559 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2567 static int uds_connectsock(int *isLocal, char *sockpath)
2569 struct sockaddr_un addr;
2572 memset(&addr, 0, sizeof(addr));
2573 addr.sun_family = AF_UNIX;
2574 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2576 s = socket(AF_UNIX, SOCK_STREAM, 0);
2581 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2592 * input binary data from socket
2594 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2596 unsigned int len, rlen;
2598 #if defined(HAVE_OPENSSL)
2600 serv_read_ssl(ipc, buf, bytes);
2605 while (len < bytes) {
2606 rlen = read(ipc->sock, &buf[len], bytes - len);
2608 connection_died(ipc, 0);
2617 * send binary to server
2619 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2621 unsigned int bytes_written = 0;
2624 #if defined(HAVE_OPENSSL)
2626 serv_write_ssl(ipc, buf, nbytes);
2630 while (bytes_written < nbytes) {
2631 retval = write(ipc->sock, &buf[bytes_written],
2632 nbytes - bytes_written);
2634 connection_died(ipc, 0);
2637 bytes_written += retval;
2644 * input binary data from encrypted connection
2646 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2652 while (len < bytes) {
2653 if (SSL_want_read(ipc->ssl)) {
2654 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2655 error_printf("SSL_write in serv_read:\n");
2656 ERR_print_errors_fp(stderr);
2659 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2663 errval = SSL_get_error(ipc->ssl, rlen);
2664 if (errval == SSL_ERROR_WANT_READ ||
2665 errval == SSL_ERROR_WANT_WRITE) {
2670 Not sure why we'd want to handle these error codes any differently,
2671 but this definitely isn't the way to handle them. Someone must have
2672 naively assumed that we could fall back to unencrypted communications,
2673 but all it does is just recursively blow the stack.
2674 if (errval == SSL_ERROR_ZERO_RETURN ||
2675 errval == SSL_ERROR_SSL) {
2676 serv_read(ipc, &buf[len], bytes - len);
2680 error_printf("SSL_read in serv_read: %s\n",
2681 ERR_reason_error_string(ERR_peek_error()));
2682 connection_died(ipc, 1);
2691 * send binary to server encrypted
2693 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2695 unsigned int bytes_written = 0;
2699 while (bytes_written < nbytes) {
2700 if (SSL_want_write(ipc->ssl)) {
2701 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2702 error_printf("SSL_read in serv_write:\n");
2703 ERR_print_errors_fp(stderr);
2706 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2707 nbytes - bytes_written);
2711 errval = SSL_get_error(ipc->ssl, retval);
2712 if (errval == SSL_ERROR_WANT_READ ||
2713 errval == SSL_ERROR_WANT_WRITE) {
2717 if (errval == SSL_ERROR_ZERO_RETURN ||
2718 errval == SSL_ERROR_SSL) {
2719 serv_write(ipc, &buf[bytes_written],
2720 nbytes - bytes_written);
2723 error_printf("SSL_write in serv_write: %s\n",
2724 ERR_reason_error_string(ERR_peek_error()));
2725 connection_died(ipc, 1);
2728 bytes_written += retval;
2733 static void CtdlIPC_init_OpenSSL(void)
2736 SSL_METHOD *ssl_method;
2739 /* already done init */
2748 SSL_load_error_strings();
2749 SSLeay_add_ssl_algorithms();
2751 /* Set up the SSL context in which we will oeprate */
2752 ssl_method = SSLv23_client_method();
2753 ssl_ctx = SSL_CTX_new(ssl_method);
2755 error_printf("SSL_CTX_new failed: %s\n",
2756 ERR_reason_error_string(ERR_get_error()));
2759 /* Any reasonable cipher we can get */
2760 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2761 error_printf("No ciphers available for encryption\n");
2764 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2766 /* Load DH parameters into the context */
2769 error_printf("Can't allocate a DH object: %s\n",
2770 ERR_reason_error_string(ERR_get_error()));
2773 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2774 error_printf("Can't assign DH_P: %s\n",
2775 ERR_reason_error_string(ERR_get_error()));
2779 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2780 error_printf("Can't assign DH_G: %s\n",
2781 ERR_reason_error_string(ERR_get_error()));
2786 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2789 #ifdef THREADED_CLIENT
2790 /* OpenSSL requires callbacks for threaded clients */
2791 CRYPTO_set_locking_callback(ssl_lock);
2792 CRYPTO_set_id_callback(id_callback);
2794 /* OpenSSL requires us to do semaphores for threaded clients */
2795 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2797 perror("malloc failed");
2800 for (a = 0; a < CRYPTO_num_locks(); a++) {
2801 Critters[a] = malloc(sizeof (pthread_mutex_t));
2803 perror("malloc failed");
2806 pthread_mutex_init(Critters[a], NULL);
2809 #endif /* THREADED_CLIENT */
2813 static void ssl_lock(int mode, int n, const char *file, int line)
2815 #ifdef THREADED_CLIENT
2816 if (mode & CRYPTO_LOCK)
2817 pthread_mutex_lock(Critters[n]);
2819 pthread_mutex_unlock(Critters[n]);
2820 #endif /* THREADED_CLIENT */
2823 #ifdef THREADED_CLIENT
2824 static unsigned long id_callback(void) {
2825 return (unsigned long)pthread_self();
2827 #endif /* THREADED_CLIENT */
2828 #endif /* HAVE_OPENSSL */
2832 * input string from socket - implemented in terms of serv_read()
2834 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2838 /* Read one character at a time. */
2840 serv_read(ipc, &buf[i], 1);
2841 if (buf[i] == '\n' || i == (SIZ-1))
2845 /* If we got a long line, discard characters until the newline. */
2847 while (buf[i] != '\n')
2848 serv_read(ipc, &buf[i], 1);
2850 /* Strip the trailing newline (and carriage return, if present) */
2851 if (buf[i] == 10) buf[i--] = 0;
2852 if (buf[i] == 13) buf[i--] = 0;
2855 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2857 CtdlIPC_getline(ipc, buf);
2861 * send line to server - implemented in terms of serv_write()
2863 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2869 cmd = malloc(len + 2);
2871 /* This requires no extra memory */
2872 serv_write(ipc, buf, len);
2873 serv_write(ipc, "\n", 1);
2875 /* This is network-optimized */
2876 strncpy(cmd, buf, len);
2877 strcpy(cmd + len, "\n");
2878 serv_write(ipc, cmd, len + 1);
2882 ipc->last_command_sent = time(NULL);
2885 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2887 CtdlIPC_putline(ipc, buf);
2894 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2902 ipc = ialloc(CtdlIPC);
2906 #if defined(HAVE_OPENSSL)
2908 CtdlIPC_init_OpenSSL();
2910 #if defined(HAVE_PTHREAD_H)
2911 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2913 ipc->sock = -1; /* Not connected */
2914 ipc->isLocal = 0; /* Not local, of course! */
2915 ipc->downloading = 0;
2917 ipc->last_command_sent = 0L;
2918 ipc->network_status_cb = NULL;
2920 strcpy(cithost, DEFAULT_HOST); /* default host */
2921 strcpy(citport, DEFAULT_PORT); /* default port */
2923 /* Allow caller to supply our values (Windows) */
2924 if (hostbuf && strlen(hostbuf) > 0)
2925 strcpy(cithost, hostbuf);
2926 if (portbuf && strlen(portbuf) > 0)
2927 strcpy(citport, portbuf);
2929 /* Read host/port from command line if present */
2930 for (a = 0; a < argc; ++a) {
2933 } else if (a == 1) {
2934 strcpy(cithost, argv[a]);
2935 } else if (a == 2) {
2936 strcpy(citport, argv[a]);
2938 error_printf("%s: usage: ",argv[0]);
2939 error_printf("%s [host] [port] ",argv[0]);
2946 if ((!strcmp(cithost, "localhost"))
2947 || (!strcmp(cithost, "127.0.0.1"))) {
2951 /* If we're using a unix domain socket we can do a bunch of stuff */
2952 if (!strcmp(cithost, UDS)) {
2953 if (!strcasecmp(citport, DEFAULT_PORT)) {
2954 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2957 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2959 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2960 if (ipc->sock == -1) {
2964 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2965 if (portbuf != NULL) strcpy(portbuf, sockpath);
2969 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2970 if (ipc->sock == -1) {
2974 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2975 if (portbuf != NULL) strcpy(portbuf, citport);
2981 * Disconnect and delete the IPC class (destructor)
2983 void CtdlIPC_delete(CtdlIPC* ipc)
2987 SSL_shutdown(ipc->ssl);
2992 if (ipc->sock > -1) {
2993 shutdown(ipc->sock, 2); /* Close it up */
3001 * Disconnect and delete the IPC class (destructor)
3002 * Also NULLs out the pointer
3004 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3006 CtdlIPC_delete(*pipc);
3012 * return the file descriptor of the server socket so we can select() on it.
3014 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3017 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3024 * return one character
3026 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3029 char CtdlIPC_get(CtdlIPC* ipc)
3034 serv_read(ipc, buf, 1);