]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
* New experimental message formatter - try it, you'll like it!
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #define UDS                     "_UDS_"
4 #define DEFAULT_HOST            UDS
5 #define DEFAULT_PORT            "citadel"
6
7 #include "sysdep.h"
8 #if TIME_WITH_SYS_TIME
9 # include <sys/time.h>
10 # include <time.h>
11 #else
12 # if HAVE_SYS_TIME_H
13 #  include <sys/time.h>
14 # else
15 #  include <time.h>
16 # endif
17 #endif
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <string.h>
22 #ifdef HAVE_MALLOC_H
23 #include <malloc.h>
24 #endif
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <sys/un.h>
32 #include <errno.h>
33 #ifdef THREADED_CLIENT
34 #include <pthread.h>
35 #endif
36 #include "citadel.h"
37 #include "citadel_ipc.h"
38 #include "citadel_decls.h"
39 #include "tools.h"
40
41 #ifdef THREADED_CLIENT
42 pthread_mutex_t rwlock;
43 #endif
44
45 #ifdef HAVE_OPENSSL
46 static SSL_CTX *ssl_ctx;
47 char arg_encrypt;
48 char rc_encrypt;
49 #ifdef THREADED_CLIENT
50 pthread_mutex_t **Critters;                     /* Things that need locking */
51 #endif /* THREADED_CLIENT */
52
53 #endif /* HAVE_OPENSSL */
54
55 #ifndef INADDR_NONE
56 #define INADDR_NONE 0xffffffff
57 #endif
58
59 static void (*status_hook)(char *s) = NULL;
60
61 void setCryptoStatusHook(void (*hook)(char *s)) {
62         status_hook = hook;
63 }
64
65 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
66         ipc->network_status_cb = hook;
67 }
68
69
70 char express_msgs = 0;
71
72
73 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
74 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
75 #ifdef HAVE_OPENSSL
76 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
77 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
78 static void ssl_lock(int mode, int n, const char *file, int line);
79 static void endtls(SSL *ssl);
80 #ifdef THREADED_CLIENT
81 static unsigned long id_callback(void);
82 #endif /* THREADED_CLIENT */
83 #endif /* HAVE_OPENSSL */
84
85
86 /*
87  * Does nothing.  The server should always return 200.
88  */
89 int CtdlIPCNoop(CtdlIPC *ipc)
90 {
91         char aaa[128];
92
93         return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
94 }
95
96
97 /*
98  * Does nothing interesting.  The server should always return 200
99  * along with your string.
100  */
101 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
102 {
103         register int ret;
104         char *aaa;
105         
106         if (!arg) return -2;
107         if (!cret) return -2;
108
109         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110         if (!aaa) return -1;
111
112         sprintf(aaa, "ECHO %s", arg);
113         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
114         free(aaa);
115         return ret;
116 }
117
118
119 /*
120  * Asks the server to close the connecction.
121  * Should always return 200.
122  */
123 int CtdlIPCQuit(CtdlIPC *ipc)
124 {
125         register int ret;
126         char aaa[128];
127
128         CtdlIPC_lock(ipc);
129         CtdlIPC_putline(ipc, "QUIT");
130         CtdlIPC_getline(ipc, aaa);
131         ret = atoi(aaa);
132         CtdlIPC_unlock(ipc);
133         return ret;
134 }
135
136
137 /*
138  * Asks the server to logout.  Should always return 200, even if no user
139  * was logged in.  The user will not be logged in after this!
140  */
141 int CtdlIPCLogout(CtdlIPC *ipc)
142 {
143         register int ret;
144         char aaa[128];
145
146         CtdlIPC_lock(ipc);
147         CtdlIPC_putline(ipc, "LOUT");
148         CtdlIPC_getline(ipc, aaa);
149         ret = atoi(aaa);
150         CtdlIPC_unlock(ipc);
151         return ret;
152 }
153
154
155 /*
156  * First stage of authentication - pass the username.  Returns 300 if the
157  * username is able to log in, with the username correctly spelled in cret.
158  * Returns various 500 error codes if the user doesn't exist, etc.
159  */
160 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
161 {
162         register int ret;
163         char *aaa;
164
165         if (!username) return -2;
166         if (!cret) return -2;
167
168         aaa = (char *)malloc((size_t)(strlen(username) + 6));
169         if (!aaa) return -1;
170
171         sprintf(aaa, "USER %s", username);
172         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
173         free(aaa);
174         return ret;
175 }
176
177
178 /*
179  * Second stage of authentication - provide password.  The server returns
180  * 200 and several arguments in cret relating to the user's account.
181  */
182 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
183 {
184         register int ret;
185         char *aaa;
186
187         if (!passwd) return -2;
188         if (!cret) return -2;
189
190         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
191         if (!aaa) return -1;
192
193         sprintf(aaa, "PASS %s", passwd);
194         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
195         free(aaa);
196         return ret;
197 }
198
199
200 /*
201  * Second stage of authentication - provide password.  The server returns
202  * 200 and several arguments in cret relating to the user's account.
203  */
204 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
205 {
206         register int ret;
207         char *aaa;
208
209         if (!response) return -2;
210         if (!cret) return -2;
211
212         aaa = (char *)malloc((size_t)(strlen(response) + 6));
213         if (!aaa) return -1;
214
215         sprintf(aaa, "PAS2 %s", response);
216         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
217         free(aaa);
218         return ret;
219 }
220
221
222 /*
223  * Create a new user.  This returns 200 plus the same arguments as TryPassword
224  * if selfservice is nonzero, unless there was a problem creating the account.
225  * If selfservice is zero, creates a new user but does not log out the existing
226  * user - intended for use by system administrators to create accounts on
227  * behalf of other users.
228  */
229 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
230 {
231         register int ret;
232         char *aaa;
233
234         if (!username) return -2;
235         if (!cret) return -2;
236
237         aaa = (char *)malloc((size_t)(strlen(username) + 6));
238         if (!aaa) return -1;
239
240         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
241         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
242         free(aaa);
243         return ret;
244 }
245
246
247 /*
248  * Changes the user's password.  Returns 200 if changed, errors otherwise.
249  */
250 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
251 {
252         register int ret;
253         char *aaa;
254
255         if (!passwd) return -2;
256         if (!cret) return -2;
257
258         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
259         if (!aaa) return -1;
260
261         sprintf(aaa, "SETP %s", passwd);
262         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
263         free(aaa);
264         return ret;
265 }
266
267
268 /* LKRN */
269 /* Caller must free the march list */
270 /* Room types are defined in enum RoomList; keep these in sync! */
271 /* floor is -1 for all, or floornum */
272 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
273 {
274         register int ret;
275         struct march *march = NULL;
276         static char *proto[] =
277                 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
278         char aaa[SIZ];
279         char *bbb = NULL;
280         size_t bbbsize;
281
282         if (!listing) return -2;
283         if (*listing) return -2;        /* Free the listing first */
284         if (!cret) return -2;
285         /* if (which < 0 || which > 4) return -2; */
286         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
287
288         sprintf(aaa, "%s %d", proto[which], floor);
289         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
290         if (ret / 100 == 1) {
291                 struct march *mptr;
292
293                 while (bbb && strlen(bbb)) {
294                         int a;
295
296                         extract_token(aaa, bbb, 0, '\n');
297                         a = strlen(aaa);
298                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
299                         mptr = (struct march *) malloc(sizeof (struct march));
300                         if (mptr) {
301                                 mptr->next = NULL;
302                                 extract(mptr->march_name, aaa, 0);
303                                 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
304                                 mptr->march_floor = (char) extract_int(aaa, 2);
305                                 mptr->march_order = (char) extract_int(aaa, 3);
306                                 if (march == NULL)
307                                         march = mptr;
308                                 else {
309                                         struct march *mptr2;
310
311                                         mptr2 = march;
312                                         while (mptr2->next != NULL)
313                                                 mptr2 = mptr2->next;
314                                         mptr2->next = mptr;
315                                 }
316                         }
317                 }
318         }
319         *listing = march;
320         return ret;
321 }
322
323
324 /* GETU */
325 /* Caller must free the struct usersupp; caller may pass an existing one */
326 int CtdlIPCGetConfig(CtdlIPC *ipc, struct usersupp **uret, char *cret)
327 {
328         register int ret;
329
330         if (!cret) return -2;
331         if (!uret) return -2;
332         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
333         if (!*uret) return -1;
334
335         ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
336         if (ret / 100 == 2) {
337                 uret[0]->USscreenwidth = extract_int(cret, 0);
338                 uret[0]->USscreenheight = extract_int(cret, 1);
339                 uret[0]->flags = extract_int(cret, 2);
340         }
341         return ret;
342 }
343
344
345 /* SETU */
346 int CtdlIPCSetConfig(CtdlIPC *ipc, struct usersupp *uret, char *cret)
347 {
348         char aaa[48];
349
350         if (!uret) return -2;
351         if (!cret) return -2;
352
353         sprintf(aaa, "SETU %d|%d|%d",
354                         uret->USscreenwidth, uret->USscreenheight,
355                         uret->flags);
356         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
357 }
358
359
360 /* GOTO */
361 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
362                 struct ctdlipcroom **rret, char *cret)
363 {
364         register int ret;
365         char *aaa;
366
367         if (!cret) return -2;
368         if (!rret) return -2;
369         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
370         if (!*rret) return -1;
371
372         if (passwd) {
373                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
374                 if (!aaa) {
375                         free(*rret);
376                         return -1;
377                 }
378                 sprintf(aaa, "GOTO %s|%s", room, passwd);
379         } else {
380                 aaa = (char *)malloc(strlen(room) + 6);
381                 if (!aaa) {
382                         free(*rret);
383                         return -1;
384                 }
385                 sprintf(aaa, "GOTO %s", room);
386         }
387         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
388         if (ret / 100 == 2) {
389                 extract(rret[0]->RRname, cret, 0);
390                 rret[0]->RRunread = extract_long(cret, 1);
391                 rret[0]->RRtotal = extract_long(cret, 2);
392                 rret[0]->RRinfoupdated = extract_int(cret, 3);
393                 rret[0]->RRflags = extract_int(cret, 4);
394                 rret[0]->RRhighest = extract_long(cret, 5);
395                 rret[0]->RRlastread = extract_long(cret, 6);
396                 rret[0]->RRismailbox = extract_int(cret, 7);
397                 rret[0]->RRaide = extract_int(cret, 8);
398                 rret[0]->RRnewmail = extract_long(cret, 9);
399                 rret[0]->RRfloor = extract_int(cret, 10);
400         } else {
401                 free(*rret);
402         }
403         return ret;
404 }
405
406
407 /* MSGS */
408 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
409 /* whicharg is number of messages, applies to last, first, gt, lt */
410 int CtdlIPCGetMessages(CtdlIPC *ipc, int which, int whicharg,
411                 const char *mtemplate, long **mret, char *cret)
412 {
413         register int ret;
414         register unsigned long count = 0;
415         static char *proto[] =
416                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
417         char aaa[33];
418         char *bbb;
419         size_t bbbsize;
420
421         if (!cret) return -2;
422         if (!mret) return -2;
423         if (*mret) return -2;
424         if (which < 0 || which > 6) return -2;
425
426         if (which <= 2)
427                 sprintf(aaa, "MSGS %s||%d", proto[which],
428                                 (mtemplate) ? 1 : 0);
429         else
430                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
431                                 (mtemplate) ? 1 : 0);
432         if (mtemplate) count = strlen(mtemplate);
433         ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbbsize, cret);
434         count = 0;
435         while (strlen(bbb)) {
436                 int a;
437
438                 extract_token(aaa, bbb, 0, '\n');
439                 a = strlen(aaa);
440                 memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
441                 *mret = (long *)realloc(mret,
442                                         (size_t)((count + 1) * sizeof (long)));
443                 if (*mret)
444                         *mret[count++] = atol(aaa);
445                 *mret[count] = 0L;
446         }
447         return ret;
448 }
449
450
451 /* MSG0, MSG2 */
452 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
453                 struct ctdlipcmessage **mret, char *cret)
454 {
455         register int ret;
456         char aaa[SIZ];
457         char *bbb = NULL;
458         size_t bbbsize;
459         int multipart_hunting = 0;
460         char multipart_prefix[SIZ];
461
462         if (!cret) return -1;
463         if (!mret) return -1;
464         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
465         if (!*mret) return -1;
466         if (!msgnum) return -1;
467
468         strcpy(mret[0]->content_type, "");
469         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
470         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
471         if (ret / 100 == 1) {
472                 if (as_mime != 2) {
473                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
474                         while (strlen(bbb) > 4 && bbb[4] == '=') {
475                                 extract_token(aaa, bbb, 0, '\n');
476                                 remove_token(bbb, 0, '\n');
477
478                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
479                                         mret[0]->nhdr = 1;
480                                 else if (!strncasecmp(aaa, "from=", 5))
481                                         strcpy(mret[0]->author, &aaa[5]);
482                                 else if (!strncasecmp(aaa, "type=", 5))
483                                         mret[0]->type = atoi(&aaa[5]);
484                                 else if (!strncasecmp(aaa, "msgn=", 5))
485                                         strcpy(mret[0]->msgid, &aaa[5]);
486                                 else if (!strncasecmp(aaa, "subj=", 5))
487                                         strcpy(mret[0]->subject, &aaa[5]);
488                                 else if (!strncasecmp(aaa, "rfca=", 5))
489                                         strcpy(mret[0]->email, &aaa[5]);
490                                 else if (!strncasecmp(aaa, "hnod=", 5))
491                                         strcpy(mret[0]->hnod, &aaa[5]);
492                                 else if (!strncasecmp(aaa, "room=", 5))
493                                         strcpy(mret[0]->room, &aaa[5]);
494                                 else if (!strncasecmp(aaa, "node=", 5))
495                                         strcpy(mret[0]->node, &aaa[5]);
496                                 else if (!strncasecmp(aaa, "rcpt=", 5))
497                                         strcpy(mret[0]->recipient, &aaa[5]);
498                                 else if (!strncasecmp(aaa, "time=", 5))
499                                         mret[0]->time = atol(&aaa[5]);
500
501                                 /* Multipart/alternative prefix & suffix strings help
502                                  * us to determine which part we want to download.
503                                  */
504                                 else if (!strncasecmp(aaa, "pref=", 5)) {
505                                         extract(multipart_prefix, &aaa[5], 1);
506                                         if (!strcasecmp(multipart_prefix,
507                                            "multipart/alternative")) {
508                                                 ++multipart_hunting;
509                                         }
510                                 }
511                                 else if (!strncasecmp(aaa, "suff=", 5)) {
512                                         extract(multipart_prefix, &aaa[5], 1);
513                                         if (!strcasecmp(multipart_prefix,
514                                            "multipart/alternative")) {
515                                                 ++multipart_hunting;
516                                         }
517                                 }
518
519                                 else if (!strncasecmp(aaa, "part=", 5)) {
520                                         struct parts *ptr, *chain;
521         
522                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
523                                         if (ptr) {
524
525                                                 /* Fill the buffers for the caller */
526                                                 extract(ptr->name, &aaa[5], 0);
527                                                 extract(ptr->filename, &aaa[5], 1);
528                                                 extract(ptr->number, &aaa[5], 2);
529                                                 extract(ptr->disposition, &aaa[5], 3);
530                                                 extract(ptr->mimetype, &aaa[5], 4);
531                                                 ptr->length = extract_long(&aaa[5], 5);
532                                                 if (!mret[0]->attachments)
533                                                         mret[0]->attachments = ptr;
534                                                 else {
535                                                         chain = mret[0]->attachments;
536                                                         while (chain->next)
537                                                                 chain = chain->next;
538                                                         chain->next = ptr;
539                                                 }
540
541                                                 /* Now handle multipart/alternative */
542                                                 if (multipart_hunting > 0) {
543                                                         if ( (!strcasecmp(ptr->mimetype,
544                                                              "text/plain"))
545                                                            || (!strcasecmp(ptr->mimetype,
546                                                               "text/html")) ) {
547                                                                 strcpy(mret[0]->mime_chosen,
548                                                                         ptr->number);
549                                                         }
550                                                 }
551
552                                         }
553                                 }
554                         }
555                         /* Eliminate "text\n" */
556                         remove_token(bbb, 0, '\n');
557
558                         /* If doing a MIME thing, pull out the extra headers */
559                         if (as_mime == 4) {
560                                 do {
561                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
562                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
563                                                 strcpy(mret[0]->content_type,
564                                                         &mret[0]->content_type[14]);
565                                                 striplt(mret[0]->content_type);
566                                         }
567                                         remove_token(bbb, 0, '\n');
568                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
569                 /*ooga*/                remove_token(bbb, 0, '\n');
570                         }
571
572
573                 }
574                 if (strlen(bbb)) {
575                         /* FIXME: Strip trailing whitespace */
576                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
577                 } else {
578                         bbb = (char *)realloc(bbb, 1);
579                         *bbb = '\0';
580                 }
581                 mret[0]->text = bbb;
582         }
583         return ret;
584 }
585
586
587 /* WHOK */
588 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
589 {
590         register int ret;
591         size_t bytes;
592
593         if (!cret) return -2;
594         if (!listing) return -2;
595         if (*listing) return -2;
596
597         ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
598         return ret;
599 }
600
601
602 /* INFO */
603 int CtdlIPCServerInfo(CtdlIPC *ipc, struct CtdlServInfo *ServInfo, char *cret)
604 {
605         register int ret;
606         size_t bytes;
607         char *listing = NULL;
608         char buf[SIZ];
609
610         if (!cret) return -2;
611         if (!ServInfo) return -2;
612
613         ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
614         if (ret / 100 == 1) {
615                 int line = 0;
616
617                 while (*listing && strlen(listing)) {
618                         extract_token(buf, listing, 0, '\n');
619                         remove_token(listing, 0, '\n');
620                         switch (line++) {
621                         case 0:         ServInfo->serv_pid = atoi(buf);
622                                         break;
623                         case 1:         strcpy(ServInfo->serv_nodename,buf);
624                                         break;
625                         case 2:         strcpy(ServInfo->serv_humannode,buf);
626                                         break;
627                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
628                                         break;
629                         case 4:         strcpy(ServInfo->serv_software,buf);
630                                         break;
631                         case 5:         ServInfo->serv_rev_level = atoi(buf);
632                                         break;
633                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
634                                         break;
635                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
636                                         break;
637                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
638                                         break;
639                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
640                                         break;
641                         case 11:        ServInfo->serv_paging_level = atoi(buf);
642                                         break;
643                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
644                                         break;
645                         }
646                 }
647
648         }
649         return ret;
650 }
651
652
653 /* RDIR */
654 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
655 {
656         register int ret;
657         size_t bytes;
658
659         if (!cret) return -2;
660         if (!listing) return -2;
661         if (*listing) return -2;
662
663         ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
664         return ret;
665 }
666
667
668 /*
669  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
670  */
671 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
672 {
673         register int ret;
674         char aaa[16];
675
676         if (!cret) return -2;
677
678         if (msgnum)
679                 sprintf(aaa, "SLRP %ld", msgnum);
680         else
681                 sprintf(aaa, "SLRP HIGHEST");
682         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
683         return ret;
684 }
685
686
687 /* INVT */
688 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
689 {
690         register int ret;
691         char *aaa;
692
693         if (!cret) return -2;
694         if (!username) return -2;
695
696         aaa = (char *)malloc(strlen(username) + 6);
697         if (!aaa) return -1;
698
699         sprintf(aaa, "INVT %s", username);
700         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
701         free(aaa);
702         return ret;
703 }
704
705
706 /* KICK */
707 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
708 {
709         register int ret;
710         char *aaa;
711
712         if (!cret) return -1;
713         if (!username) return -1;
714
715         aaa = (char *)malloc(strlen(username) + 6);
716
717         sprintf(aaa, "KICK %s", username);
718         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
719         free(aaa);
720         return ret;
721 }
722
723
724 /* GETR */
725 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
726 {
727         register int ret;
728
729         if (!cret) return -2;
730         if (!qret) return -2;
731         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
732         if (!*qret) return -1;
733
734         ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
735         if (ret / 100 == 2) {
736                 extract(qret[0]->QRname, cret, 0);
737                 extract(qret[0]->QRpasswd, cret, 1);
738                 extract(qret[0]->QRdirname, cret, 2);
739                 qret[0]->QRflags = extract_int(cret, 3);
740                 qret[0]->QRfloor = extract_int(cret, 4);
741                 qret[0]->QRorder = extract_int(cret, 5);
742                 qret[0]->QRdefaultview = extract_int(cret, 6);
743                 qret[0]->QRflags2 = extract_int(cret, 7);
744         }
745         return ret;
746 }
747
748
749 /* SETR */
750 /* set forget to kick all users out of room */
751 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct quickroom *qret, char *cret)
752 {
753         register int ret;
754         char *aaa;
755
756         if (!cret) return -2;
757         if (!qret) return -2;
758
759         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
760                         strlen(qret->QRdirname) + 52);
761         if (!aaa) return -1;
762
763         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
764                         qret->QRname, qret->QRpasswd, qret->QRdirname,
765                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
766         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
767         free(aaa);
768         return ret;
769 }
770
771
772 /* GETA */
773 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
774 {
775         if (!cret) return -1;
776
777         return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
778 }
779
780
781 /* SETA */
782 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
783 {
784         register int ret;
785         char *aaa;
786
787         if (!cret) return -2;
788         if (!username) return -2;
789
790         aaa = (char *)malloc(strlen(username) + 6);
791         if (!aaa) return -1;
792
793         sprintf(aaa, "SETA %s", username);
794         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
795         free(aaa);
796         return ret;
797 }
798
799
800 /* ENT0 */
801 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
802 {
803         register int ret;
804         char *aaa;
805
806         if (!cret) return -2;
807         if (!mr) return -2;
808
809         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
810         if (!aaa) return -1;
811
812         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
813                         mr->type, mr->author);
814         ret = CtdlIPCGenericCommand(ipc, aaa, mr->text, strlen(mr->text), NULL,
815                         NULL, cret);
816         free(aaa);
817         return ret;
818 }
819
820
821 /* RINF */
822 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
823 {
824         size_t bytes;
825
826         if (!cret) return -2;
827         if (!iret) return -2;
828         if (*iret) return -2;
829
830         return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
831 }
832
833
834 /* DELE */
835 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
836 {
837         char aaa[16];
838
839         if (!cret) return -2;
840         if (!msgnum) return -2;
841
842         sprintf(aaa, "DELE %ld", msgnum);
843         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
844 }
845
846
847 /* MOVE */
848 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
849 {
850         register int ret;
851         char *aaa;
852
853         if (!cret) return -2;
854         if (!destroom) return -2;
855         if (!msgnum) return -2;
856
857         aaa = (char *)malloc(strlen(destroom) + 28);
858         if (!aaa) return -1;
859
860         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
861         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
862         free(aaa);
863         return ret;
864 }
865
866
867 /* KILL */
868 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
869 {
870         char aaa[16];
871
872         if (!cret) return -2;
873
874         sprintf(aaa, "KILL %d", for_real);
875         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
876 }
877
878
879 /* CRE8 */
880 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
881                 const char *password, int floor, char *cret)
882 {
883         register int ret;
884         char *aaa;
885
886         if (!cret) return -2;
887         if (!roomname) return -2;
888
889         if (password) {
890                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
891                 if (!aaa) return -1;
892                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
893                                 password, floor);
894         } else {
895                 aaa = (char *)malloc(strlen(roomname) + 40);
896                 if (!aaa) return -1;
897                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
898                                 floor);
899         }
900         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
901         free(aaa);
902         return ret;
903 }
904
905
906 /* FORG */
907 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
908 {
909         if (!cret) return -2;
910
911         return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
912 }
913
914
915 /* MESG */
916 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
917 {
918         register int ret;
919         char *aaa;
920         size_t bytes;
921
922         if (!cret) return -2;
923         if (!mret) return -2;
924         if (*mret) return -2;
925         if (!message) return -2;
926
927         aaa = (char *)malloc(strlen(message) + 6);
928         if (!aaa) return -1;
929
930         sprintf(aaa, "MESG %s", message);
931         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
932         free(aaa);
933         return ret;
934 }
935
936
937 /* GNUR */
938 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
939 {
940         if (!cret) return -2;
941
942         return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
943 }
944
945
946 /* GREG */
947 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
948 {
949         register int ret;
950         char *aaa;
951         size_t bytes;
952
953         if (!cret) return -2;
954         if (!rret) return -2;
955         if (*rret) return -2;
956
957         if (username)
958                 aaa = (char *)malloc(strlen(username) + 6);
959         else
960                 aaa = (char *)malloc(12);
961         if (!aaa) return -1;
962
963         if (username)
964                 sprintf(aaa, "GREG %s", username);
965         else
966                 sprintf(aaa, "GREG _SELF_");
967         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
968         free(aaa);
969         return ret;
970 }
971
972
973 /* VALI */
974 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
975 {
976         register int ret;
977         char *aaa;
978
979         if (!cret) return -2;
980         if (!username) return -2;
981         if (axlevel < 0 || axlevel > 7) return -2;
982
983         aaa = (char *)malloc(strlen(username) + 17);
984         if (!aaa) return -1;
985
986         sprintf(aaa, "VALI %s|%d", username, axlevel);
987         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
988         free(aaa);
989         return ret;
990 }
991
992
993 /* EINF */
994 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
995 {
996         char aaa[16];
997
998         if (!cret) return -1;
999         if (!info) return -1;
1000
1001         sprintf(aaa, "EINF %d", for_real);
1002         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1003 }
1004
1005
1006 /* LIST */
1007 int CtdlIPCUserListing(CtdlIPC *ipc, char **listing, char *cret)
1008 {
1009         size_t bytes;
1010
1011         if (!cret) return -1;
1012         if (!listing) return -1;
1013         if (*listing) return -1;
1014
1015         return CtdlIPCGenericCommand(ipc, "LIST", NULL, 0, listing, &bytes, cret);
1016 }
1017
1018
1019 /* REGI */
1020 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1021 {
1022         if (!cret) return -1;
1023         if (!info) return -1;
1024
1025         return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1026                         NULL, NULL, cret);
1027 }
1028
1029
1030 /* CHEK */
1031 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1032 {
1033         register int ret;
1034
1035         if (!cret) return -1;
1036         if (!chek) return -1;
1037
1038         ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1039         if (ret / 100 == 2) {
1040                 chek->newmail = extract_long(cret, 0);
1041                 chek->needregis = extract_int(cret, 1);
1042                 chek->needvalid = extract_int(cret, 2);
1043         }
1044         return ret;
1045 }
1046
1047
1048 /* DELF */
1049 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1050 {
1051         register int ret;
1052         char *aaa;
1053
1054         if (!cret) return -2;
1055         if (!filename) return -2;
1056         
1057         aaa = (char *)malloc(strlen(filename) + 6);
1058         if (!aaa) return -1;
1059
1060         sprintf(aaa, "DELF %s", filename);
1061         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1062         free(aaa);
1063         return ret;
1064 }
1065
1066
1067 /* MOVF */
1068 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1069 {
1070         register int ret;
1071         char *aaa;
1072
1073         if (!cret) return -2;
1074         if (!filename) return -2;
1075         if (!destroom) return -2;
1076
1077         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1078         if (!aaa) return -1;
1079
1080         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1081         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1082         free(aaa);
1083         return ret;
1084 }
1085
1086
1087 /* NETF */
1088 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1089 {
1090         register int ret;
1091         char *aaa;
1092
1093         if (!cret) return -2;
1094         if (!filename) return -2;
1095         if (!destnode) return -2;
1096
1097         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1098         if (!aaa) return -1;
1099
1100         sprintf(aaa, "NETF %s|%s", filename, destnode);
1101         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1102         free(aaa);
1103         return ret;
1104 }
1105
1106
1107 /* RWHO */
1108 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1109 {
1110         register int ret;
1111         size_t bytes;
1112
1113         if (!cret) return -1;
1114         if (!listing) return -1;
1115         if (*listing) return -1;
1116
1117         *stamp = CtdlIPCServerTime(ipc, cret);
1118         if (!*stamp)
1119                 *stamp = time(NULL);
1120         ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1121         return ret;
1122 }
1123
1124
1125 /* OPEN */
1126 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1127                 size_t resume,
1128                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1129                 char *cret)
1130 {
1131         register int ret;
1132         size_t bytes;
1133         time_t last_mod;
1134         char mimetype[SIZ];
1135         char *aaa;
1136
1137         if (!cret) return -2;
1138         if (!filename) return -2;
1139         if (!buf) return -2;
1140         if (*buf) return -2;
1141         if (ipc->downloading) return -2;
1142
1143         aaa = (char *)malloc(strlen(filename) + 6);
1144         if (!aaa) return -1;
1145
1146         sprintf(aaa, "OPEN %s", filename);
1147         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1148         free(aaa);
1149         if (ret / 100 == 2) {
1150                 ipc->downloading = 1;
1151                 bytes = extract_long(cret, 0);
1152                 last_mod = extract_int(cret, 1);
1153                 extract(mimetype, cret, 2);
1154 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret); */
1155                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1156                 ret = CtdlIPCEndDownload(ipc, cret);
1157                 if (ret / 100 == 2)
1158                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1159                                         filename, mimetype);
1160         }
1161         return ret;
1162 }
1163
1164
1165 /* OPNA */
1166 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1167                 void **buf,
1168                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1169                 char *cret)
1170 {
1171         register int ret;
1172         size_t bytes;
1173         time_t last_mod;
1174         char filename[SIZ];
1175         char mimetype[SIZ];
1176         char *aaa;
1177
1178         if (!cret) return -2;
1179         if (!buf) return -2;
1180         if (*buf) return -2;
1181         if (!part) return -2;
1182         if (!msgnum) return -2;
1183         if (ipc->downloading) return -2;
1184
1185         aaa = (char *)malloc(strlen(part) + 17);
1186         if (!aaa) return -1;
1187
1188         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1189         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1190         free(aaa);
1191         if (ret / 100 == 2) {
1192                 ipc->downloading = 1;
1193                 bytes = extract_long(cret, 0);
1194                 last_mod = extract_int(cret, 1);
1195                 extract(mimetype, cret, 2);
1196 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1197                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1198                 ret = CtdlIPCEndDownload(ipc, cret);
1199                 if (ret / 100 == 2)
1200                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1201                                         filename, mimetype);
1202         }
1203         return ret;
1204 }
1205
1206
1207 /* OIMG */
1208 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1209                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1210                 char *cret)
1211 {
1212         register int ret;
1213         size_t bytes;
1214         time_t last_mod;
1215         char mimetype[SIZ];
1216         char *aaa;
1217
1218         if (!cret) return -1;
1219         if (!buf) return -1;
1220         if (*buf) return -1;
1221         if (!filename) return -1;
1222         if (ipc->downloading) return -1;
1223
1224         aaa = (char *)malloc(strlen(filename) + 6);
1225         if (!aaa) return -1;
1226
1227         sprintf(aaa, "OIMG %s", filename);
1228         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1229         free(aaa);
1230         if (ret / 100 == 2) {
1231                 ipc->downloading = 1;
1232                 bytes = extract_long(cret, 0);
1233                 last_mod = extract_int(cret, 1);
1234                 extract(mimetype, cret, 2);
1235 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1236                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1237                 ret = CtdlIPCEndDownload(ipc, cret);
1238                 if (ret / 100 == 2)
1239                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1240                                         filename, mimetype);
1241         }
1242         return ret;
1243 }
1244
1245
1246 /* UOPN */
1247 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1248                 const char *path,
1249                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1250                 char *cret)
1251 {
1252         register int ret;
1253         char *aaa;
1254
1255         if (!cret) return -1;
1256         if (!save_as) return -1;
1257         if (!comment) return -1;
1258         if (!path) return -1;
1259         if (!*path) return -1;
1260         if (ipc->uploading) return -1;
1261
1262         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1263         if (!aaa) return -1;
1264
1265         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1266         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1267         free(aaa);
1268         if (ret / 100 == 2) {
1269                 ipc->uploading = 1;
1270                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1271                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1272                 ipc->uploading = 0;
1273         }
1274         return ret;
1275 }
1276
1277
1278 /* UIMG */
1279 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1280                 const char *save_as,
1281                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1282                 char *cret)
1283 {
1284         register int ret;
1285         char *aaa;
1286
1287         if (!cret) return -1;
1288         if (!save_as) return -1;
1289         if (!path && for_real) return -1;
1290         if (!*path && for_real) return -1;
1291         if (ipc->uploading) return -1;
1292
1293         aaa = (char *)malloc(strlen(save_as) + 17);
1294         if (!aaa) return -1;
1295
1296         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1297         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1298         free(aaa);
1299         if (ret / 100 == 2 && for_real) {
1300                 ipc->uploading = 1;
1301                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1302                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1303                 ipc->uploading = 0;
1304         }
1305         return ret;
1306 }
1307
1308
1309 /* QUSR */
1310 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1311 {
1312         register int ret;
1313         char *aaa;
1314
1315         if (!cret) return -2;
1316         if (!username) return -2;
1317
1318         aaa = (char *)malloc(strlen(username) + 6);
1319         if (!aaa) return -1;
1320
1321         sprintf(aaa, "QUSR %s", username);
1322         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1323         free(aaa);
1324         return ret;
1325 }
1326
1327
1328 /* LFLR */
1329 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1330 {
1331         size_t bytes;
1332
1333         if (!cret) return -2;
1334         if (!listing) return -2;
1335         if (*listing) return -2;
1336
1337         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1338 }
1339
1340
1341 /* CFLR */
1342 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1343 {
1344         register int ret;
1345         char *aaa;
1346
1347         if (!cret) return -2;
1348         if (!name) return -2;
1349         if (!*name) return -2;
1350
1351         aaa = (char *)malloc(strlen(name) + 17);
1352         if (!aaa) return -1;
1353
1354         sprintf(aaa, "CFLR %s|%d", name, for_real);
1355         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1356         free(aaa);
1357         return ret;
1358 }
1359
1360
1361 /* KFLR */
1362 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1363 {
1364         char aaa[27];
1365
1366         if (!cret) return -1;
1367         if (floornum < 0) return -1;
1368
1369         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1370         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1371 }
1372
1373
1374 /* EFLR */
1375 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1376 {
1377         register int ret;
1378         char *aaa;
1379
1380         if (!cret) return -2;
1381         if (!floorname) return -2;
1382         if (floornum < 0) return -2;
1383
1384         aaa = (char *)malloc(strlen(floorname) + 17);
1385         if (!aaa) return -1;
1386
1387         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1388         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1389         free(aaa);
1390         return ret;
1391 }
1392
1393
1394 /* IDEN */
1395 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid, int revision,
1396                 const char *software_name, const char *hostname, char *cret)
1397 {
1398         register int ret;
1399         char *aaa;
1400
1401         if (developerid < 0) return -2;
1402         if (clientid < 0) return -2;
1403         if (revision < 0) return -2;
1404         if (!software_name) return -2;
1405         if (!hostname) return -2;
1406
1407         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1408         if (!aaa) return -1;
1409
1410         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1411                         revision, software_name, hostname);
1412         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1413         free(aaa);
1414         return ret;
1415 }
1416
1417
1418 /* SEXP */
1419 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1420                 char *cret)
1421 {
1422         register int ret;
1423         char *aaa;
1424
1425         if (!cret) return -2;
1426         if (!username) return -2;
1427
1428         aaa = (char *)malloc(strlen(username) + 8);
1429         if (!aaa) return -1;
1430
1431         if (text) {
1432                 sprintf(aaa, "SEXP %s|-", username);
1433                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1434                                 NULL, NULL, cret);
1435         } else {
1436                 sprintf(aaa, "SEXP %s||", username);
1437                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1438         }
1439         free(aaa);
1440         return ret;
1441 }
1442
1443
1444 /* GEXP */
1445 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1446 {
1447         size_t bytes;
1448
1449         if (!cret) return -2;
1450         if (!listing) return -2;
1451         if (*listing) return -2;
1452
1453         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1454 }
1455
1456
1457 /* DEXP */
1458 /* mode is 0 = enable, 1 = disable, 2 = status */
1459 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1460 {
1461         char aaa[16];
1462
1463         if (!cret) return -2;
1464
1465         sprintf(aaa, "DEXP %d", mode);
1466         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1467 }
1468
1469
1470 /* EBIO */
1471 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1472 {
1473         if (!cret) return -2;
1474         if (!bio) return -2;
1475
1476         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1477                         NULL, NULL, cret);
1478 }
1479
1480
1481 /* RBIO */
1482 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1483 {
1484         register int ret;
1485         size_t bytes;
1486         char *aaa;
1487
1488         if (!cret) return -2;
1489         if (!username) return -2;
1490         if (!listing) return -2;
1491         if (*listing) return -2;
1492
1493         aaa = (char *)malloc(strlen(username) + 6);
1494         if (!aaa) return -1;
1495
1496         sprintf(aaa, "RBIO %s", username);
1497         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1498         free(aaa);
1499         return ret;
1500 }
1501
1502
1503 /* LBIO */
1504 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1505 {
1506         size_t bytes;
1507
1508         if (!cret) return -2;
1509         if (!listing) return -2;
1510         if (*listing) return -2;
1511
1512         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1513 }
1514
1515
1516 /* STEL */
1517 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1518 {
1519         char aaa[16];
1520
1521         if (!cret) return -1;
1522
1523         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1524         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1525 }
1526
1527
1528 /* TERM */
1529 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1530 {
1531         char aaa[16];
1532
1533         if (!cret) return -1;
1534
1535         sprintf(aaa, "TERM %d", sid);
1536         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1537 }
1538
1539
1540 /* DOWN */
1541 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1542 {
1543         if (!cret) return -1;
1544
1545         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1546 }
1547
1548
1549 /* SCDN */
1550 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1551 {
1552         char aaa[16];
1553
1554         if (!cret) return -1;
1555
1556         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1557         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1558 }
1559
1560
1561 /* EMSG */
1562 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1563                 char *cret)
1564 {
1565         register int ret;
1566         char *aaa;
1567
1568         if (!cret) return -2;
1569         if (!text) return -2;
1570         if (!filename) return -2;
1571
1572         aaa = (char *)malloc(strlen(filename) + 6);
1573         if (!aaa) return -1;
1574
1575         sprintf(aaa, "EMSG %s", filename);
1576         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1577         free(aaa);
1578         return ret;
1579 }
1580
1581
1582 /* HCHG */
1583 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1584 {
1585         register int ret;
1586         char *aaa;
1587
1588         if (!cret) return -2;
1589         if (!hostname) return -2;
1590
1591         aaa = (char *)malloc(strlen(hostname) + 6);
1592         if (!aaa) return -1;
1593
1594         sprintf(aaa, "HCHG %s", hostname);
1595         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1596         free(aaa);
1597         return ret;
1598 }
1599
1600
1601 /* RCHG */
1602 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1603 {
1604         register int ret;
1605         char *aaa;
1606
1607         if (!cret) return -2;
1608         if (!roomname) return -2;
1609
1610         aaa = (char *)malloc(strlen(roomname) + 6);
1611         if (!aaa) return -1;
1612
1613         sprintf(aaa, "RCHG %s", roomname);
1614         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1615         free(aaa);
1616         return ret;
1617 }
1618
1619
1620 /* UCHG */
1621 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1622 {
1623         register int ret;
1624         char *aaa;
1625
1626         if (!cret) return -2;
1627         if (!username) return -2;
1628
1629         aaa = (char *)malloc(strlen(username) + 6);
1630         if (!aaa) return -1;
1631
1632         sprintf(aaa, "UCHG %s", username);
1633         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1634         free(aaa);
1635         return ret;
1636 }
1637
1638
1639 /* TIME */
1640 /* This function returns the actual server time reported, or 0 if error */
1641 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1642 {
1643         register time_t tret;
1644         register int ret;
1645
1646         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1647         if (ret / 100 == 2) {
1648                 tret = extract_long(cret, 0);
1649         } else {
1650                 tret = 0L;
1651         }
1652         return tret;
1653 }
1654
1655
1656 /* AGUP */
1657 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1658                                  struct usersupp **uret, char *cret)
1659 {
1660         register int ret;
1661         char aaa[SIZ];
1662
1663         if (!cret) return -2;
1664         if (!uret) return -2;
1665         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1666         if (!*uret) return -1;
1667
1668         sprintf(aaa, "AGUP %s", who);
1669         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1670
1671         if (ret / 100 == 2) {
1672                 extract(uret[0]->fullname, cret, 0);
1673                 extract(uret[0]->password, cret, 1);
1674                 uret[0]->flags = extract_int(cret, 2);
1675                 uret[0]->timescalled = extract_long(cret, 3);
1676                 uret[0]->posted = extract_long(cret, 4);
1677                 uret[0]->axlevel = extract_int(cret, 5);
1678                 uret[0]->usernum = extract_long(cret, 6);
1679                 uret[0]->lastcall = extract_long(cret, 7);
1680                 uret[0]->USuserpurge = extract_int(cret, 8);
1681         }
1682         return ret;
1683 }
1684
1685
1686 /* ASUP */
1687 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char *cret)
1688 {
1689         register int ret;
1690         char *aaa;
1691
1692         if (!cret) return -2;
1693         if (!uret) return -2;
1694
1695         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1696         if (!aaa) return -1;
1697
1698         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1699                         uret->fullname, uret->password, uret->flags,
1700                         uret->timescalled, uret->posted, uret->axlevel,
1701                         uret->usernum, uret->lastcall, uret->USuserpurge);
1702         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1703         free(aaa);
1704         return ret;
1705 }
1706
1707
1708 /* GPEX */
1709 /* which is 0 = room, 1 = floor, 2 = site */
1710 /* caller must free the struct ExpirePolicy */
1711 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1712                 struct ExpirePolicy **policy, char *cret)
1713 {
1714         static char *proto[] = {"room", "floor", "site"};
1715         char aaa[11];
1716         register int ret;
1717
1718         if (!cret) return -2;
1719         if (!policy) return -2;
1720         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1721         if (!*policy) return -1;
1722         if (which < 0 || which > 2) return -2;
1723         
1724         sprintf(aaa, "GPEX %s", proto[which]);
1725         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1726         if (ret / 100 == 2) {
1727                 policy[0]->expire_mode = extract_int(cret, 0);
1728                 policy[0]->expire_value = extract_int(cret, 1);
1729         }
1730         return ret;
1731
1732 }
1733
1734
1735 /* SPEX */
1736 /* which is 0 = room, 1 = floor, 2 = site */
1737 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1738 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1739                 struct ExpirePolicy *policy, char *cret)
1740 {
1741         char aaa[38];
1742
1743         if (!cret) return -2;
1744         if (which < 0 || which > 2) return -2;
1745         if (!policy) return -2;
1746         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1747         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1748
1749         sprintf(aaa, "SPEX %d|%d|%d", which,
1750                         policy->expire_mode, policy->expire_value);
1751         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1752 }
1753
1754
1755 /* CONF GET */
1756 int CtdlGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1757 {
1758         size_t bytes;
1759
1760         if (!cret) return -2;
1761         if (!listing) return -2;
1762         if (*listing) return -2;
1763
1764         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1765                         listing, &bytes, cret);
1766 }
1767
1768
1769 /* CONF SET */
1770 int CtdlSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1771 {
1772         if (!cret) return -2;
1773         if (!listing) return -2;
1774
1775         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1776                         NULL, NULL, cret);
1777 }
1778
1779
1780 /* CONF GETSYS */
1781 int CtdlGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1782                 char **listing, char *cret)
1783 {
1784         char *aaa;
1785         size_t bytes;
1786
1787         if (!cret) return -2;
1788         if (!mimetype) return -2;
1789         if (!listing) return -2;
1790         if (*listing) return -2;
1791
1792         aaa = malloc(strlen(mimetype) + 13);
1793         if (!aaa) return -1;
1794         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1795         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1796                         listing, &bytes, cret);
1797 }
1798
1799
1800 /* CONF PUTSYS */
1801 int CtdlSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1802                const char *listing, char *cret)
1803 {
1804         char *aaa;
1805
1806         if (!cret) return -2;
1807         if (!mimetype) return -2;
1808         if (!listing) return -2;
1809
1810         aaa = malloc(strlen(mimetype) + 13);
1811         if (!aaa) return -1;
1812         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1813         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1814                         NULL, NULL, cret);
1815 }
1816
1817 /* MMOD */
1818 int CtdlIPCModerateMessage(CtdlIPC *ipc, long msgnum, int level, char *cret)
1819 {
1820         char aaa[27];
1821
1822         if (!cret) return -2;
1823         if (!msgnum) return -2;
1824
1825         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1826         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1827 }
1828
1829
1830 /* REQT */
1831 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1832 {
1833         char aaa[16];
1834
1835         if (!cret) return -2;
1836         if (session < 0) return -2;
1837
1838         sprintf(aaa, "REQT %d", session);
1839         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1840 }
1841
1842
1843 /* SEEN */
1844 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1845 {
1846         char aaa[27];
1847
1848         if (!cret) return -2;
1849         if (msgnum < 0) return -2;
1850
1851         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1852         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1853 }
1854
1855
1856 /* STLS */
1857 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1858 {
1859         int a;
1860         int r;
1861         char buf[SIZ];
1862
1863 #ifdef HAVE_OPENSSL
1864         SSL *temp_ssl;
1865
1866         /* New SSL object */
1867         temp_ssl = SSL_new(ssl_ctx);
1868         if (!temp_ssl) {
1869                 error_printf("SSL_new failed: %s\n",
1870                                 ERR_reason_error_string(ERR_get_error()));
1871                 return -2;
1872         }
1873         /* Pointless flag waving */
1874 #if SSLEAY_VERSION_NUMBER >= 0x0922
1875         SSL_set_session_id_context(temp_ssl, "Citadel/UX SID", 14);
1876 #endif
1877
1878         if (!access("/var/run/egd-pool", F_OK))
1879                 RAND_egd("/var/run/egd-pool");
1880
1881         if (!RAND_status()) {
1882                 error_printf("PRNG not properly seeded\n");
1883                 return -2;
1884         }
1885
1886         /* Associate network connection with SSL object */
1887         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1888                 error_printf("SSL_set_fd failed: %s\n",
1889                                 ERR_reason_error_string(ERR_get_error()));
1890                 return -2;
1891         }
1892
1893         if (status_hook != NULL)
1894                 status_hook("Requesting encryption...\r");
1895
1896         /* Ready to start SSL/TLS */
1897         /* Old code
1898         CtdlIPC_putline(ipc, "STLS");
1899         CtdlIPC_getline(ipc, buf);
1900         if (buf[0] != '2') {
1901                 error_printf("Server can't start TLS: %s\n", buf);
1902                 return 0;
1903         }
1904         */
1905         r = CtdlIPCGenericCommand(ipc,
1906                                   "STLS", NULL, 0, NULL, NULL, cret);
1907         if (r / 100 != 2) {
1908                 error_printf("Server can't start TLS: %s\n", buf);
1909                 endtls(temp_ssl);
1910                 return r;
1911         }
1912
1913         /* Do SSL/TLS handshake */
1914         if ((a = SSL_connect(temp_ssl)) < 1) {
1915                 error_printf("SSL_connect failed: %s\n",
1916                                 ERR_reason_error_string(ERR_get_error()));
1917                 endtls(temp_ssl);
1918                 return -2;
1919         }
1920         ipc->ssl = temp_ssl;
1921
1922         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1923         {
1924                 int bits, alg_bits;
1925
1926                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1927                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1928                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1929                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1930                                 bits, alg_bits);
1931         }
1932         return r;
1933 #else
1934         return 0;
1935 #endif /* HAVE_OPENSSL */
1936 }
1937
1938
1939 #ifdef HAVE_OPENSSL
1940 static void endtls(SSL *ssl)
1941 {
1942         if (ssl) {
1943                 SSL_shutdown(ssl);
1944                 SSL_free(ssl);
1945         }
1946 }
1947 #endif
1948
1949
1950 /* QDIR */
1951 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
1952 {
1953         char *aaa;
1954
1955         if (!address) return -2;
1956         if (!cret) return -2;
1957
1958         aaa = (char *)malloc(strlen(address) + 6);
1959         if (!aaa) return -1;
1960
1961         sprintf(aaa, "QDIR %s", address);
1962         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1963 }
1964
1965
1966 /* IPGM */
1967 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
1968 {
1969         char aaa[30];
1970
1971         if (!cret) return -2;
1972         sprintf(aaa, "IPGM %d", secret);
1973         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1974 }
1975
1976
1977 /*
1978  * Not implemented:
1979  * 
1980  * CHAT
1981  * ETLS
1982  * EXPI
1983  * GTLS
1984  * IGAB
1985  * MSG3
1986  * MSG4
1987  * NDOP
1988  * NETP
1989  * NUOP
1990  * SMTP
1991  */
1992
1993
1994 /* ************************************************************************** */
1995 /*             Stuff below this line is not for public consumption            */
1996 /* ************************************************************************** */
1997
1998
1999 inline void CtdlIPC_lock(CtdlIPC *ipc)
2000 {
2001         if (ipc->network_status_cb) ipc->network_status_cb(1);
2002 #ifdef THREADED_CLIENT
2003         pthread_mutex_lock(&(ipc->mutex));
2004 #endif
2005 }
2006
2007
2008 inline void CtdlIPC_unlock(CtdlIPC *ipc)
2009 {
2010 #ifdef THREADED_CLIENT
2011         pthread_mutex_unlock(&(ipc->mutex));
2012 #endif
2013         if (ipc->network_status_cb) ipc->network_status_cb(0);
2014 }
2015
2016
2017 /* Read a listing from the server up to 000.  Append to dest if it exists */
2018 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2019 {
2020         size_t length = 0;
2021         size_t linelength;
2022         char *ret;
2023         char aaa[SIZ];
2024
2025         ret = dest;
2026         if (ret != NULL) {
2027                 length = strlen(ret);
2028         }
2029         else {
2030                 ret = strdup("");
2031                 length = 0;
2032         }
2033
2034         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2035                 linelength = strlen(aaa);
2036                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2037                 if (ret) {
2038                         strcpy(&ret[length], aaa);
2039                         length += linelength;
2040                         strcpy(&ret[length++], "\n");
2041                 }
2042         }
2043
2044         return(ret);
2045 }
2046
2047
2048 /* Send a listing to the server; generate the ending 000. */
2049 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2050 {
2051         char *text;
2052
2053         text = (char *)malloc(strlen(listing) + 6);
2054         if (text) {
2055                 strcpy(text, listing);
2056                 while (text[strlen(text) - 1] == '\n')
2057                         text[strlen(text) - 1] = '\0';
2058                 strcat(text, "\n000");
2059                 CtdlIPC_putline(ipc, text);
2060                 free(text);
2061                 text = NULL;
2062         } else {
2063                 /* Malloc failed but we are committed to send */
2064                 /* This may result in extra blanks at the bottom */
2065                 CtdlIPC_putline(ipc, text);
2066                 CtdlIPC_putline(ipc, "000");
2067         }
2068         return 0;
2069 }
2070
2071
2072 /* Partial read of file from server */
2073 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2074 {
2075         register size_t len = 0;
2076         char aaa[SIZ];
2077
2078         if (!buf) return 0;
2079         if (!cret) return 0;
2080         if (bytes < 1) return 0;
2081         if (offset < 0) return 0;
2082
2083         CtdlIPC_lock(ipc);
2084         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2085         CtdlIPC_putline(ipc, aaa);
2086         CtdlIPC_getline(ipc, aaa);
2087         if (aaa[0] != '6')
2088                 strcpy(cret, &aaa[4]);
2089         else {
2090                 len = extract_long(&aaa[4], 0);
2091                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2092                 if (*buf) {
2093                         /* I know what I'm doing */
2094                         serv_read(ipc, (*buf + offset), len);
2095                 } else {
2096                         /* We have to read regardless */
2097                         serv_read(ipc, aaa, len);
2098                         len = 0;
2099                 }
2100         }
2101         CtdlIPC_unlock(ipc);
2102         return len;
2103 }
2104
2105
2106 /* CLOS */
2107 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2108 {
2109         register int ret;
2110
2111         if (!cret) return -2;
2112         if (!ipc->downloading) return -2;
2113
2114         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2115         if (ret / 100 == 2)
2116                 ipc->downloading = 0;
2117         return ret;
2118 }
2119
2120
2121 /* MSGP */
2122 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2123         register int ret;
2124         char cmd[SIZ];
2125         
2126         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2127         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2128         return ret;
2129 }
2130
2131
2132
2133 /* READ */
2134 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2135                void (*progress_gauge_callback)(unsigned long, unsigned long),
2136                char *cret)
2137 {
2138         register size_t len;
2139
2140         if (!cret) return -1;
2141         if (!buf) return -1;
2142         if (*buf) return -1;
2143         if (!ipc->downloading) return -1;
2144
2145         len = resume;
2146         if (progress_gauge_callback)
2147                 progress_gauge_callback(len, bytes);
2148         while (len < bytes) {
2149                 register size_t block;
2150
2151                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2152                 if (block == 0) {
2153                         free(*buf);
2154                         return 0;
2155                 }
2156                 len += block;
2157                 if (progress_gauge_callback)
2158                         progress_gauge_callback(len, bytes);
2159         }
2160         return len;
2161 }
2162
2163
2164 /* READ - pipelined */
2165 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2166                size_t resume,
2167                void (*progress_gauge_callback)(unsigned long, unsigned long),
2168                char *cret)
2169 {
2170         register size_t len;
2171         register int calls;     /* How many calls in the pipeline */
2172         register int i;         /* iterator */
2173         char aaa[4096];
2174
2175         if (!cret) return -1;
2176         if (!buf) return -1;
2177         if (*buf) return -1;
2178         if (!ipc->downloading) return -1;
2179
2180         *buf = (void *)realloc(*buf, bytes - resume);
2181         if (!*buf) return -1;
2182
2183         len = 0;
2184         CtdlIPC_lock(ipc);
2185         if (progress_gauge_callback)
2186                 progress_gauge_callback(len, bytes);
2187
2188         /* How many calls will be in the pipeline? */
2189         calls = (bytes - resume) / 4096;
2190         if ((bytes - resume) % 4096) calls++;
2191
2192         /* Send all requests at once */
2193         for (i = 0; i < calls; i++) {
2194                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2195                 CtdlIPC_putline(ipc, aaa);
2196         }
2197
2198         /* Receive all responses at once */
2199         for (i = 0; i < calls; i++) {
2200                 CtdlIPC_getline(ipc, aaa);
2201                 if (aaa[0] != '6')
2202                         strcpy(cret, &aaa[4]);
2203                 else {
2204                         len = extract_long(&aaa[4], 0);
2205                         /* I know what I'm doing */
2206                         serv_read(ipc, ((*buf) + (i * 4096)), len);
2207                 }
2208                 if (progress_gauge_callback)
2209                         progress_gauge_callback(i * 4096 + len, bytes);
2210         }
2211         CtdlIPC_unlock(ipc);
2212         return len;
2213 }
2214
2215
2216 /* UCLS */
2217 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2218 {
2219         register int ret;
2220         char cmd[8];
2221
2222         if (!cret) return -1;
2223         if (!ipc->uploading) return -1;
2224
2225         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2226         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2227         ipc->uploading = 0;
2228         return ret;
2229 }
2230
2231
2232 /* WRIT */
2233 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2234                 void (*progress_gauge_callback)(unsigned long, unsigned long),
2235                 char *cret)
2236 {
2237         register int ret = -1;
2238         register size_t offset = 0;
2239         size_t bytes;
2240         char aaa[SIZ];
2241         char buf[4096];
2242         FILE *fd;
2243
2244         if (!cret) return -1;
2245         if (!path) return -1;
2246         if (!*path) return -1;
2247
2248         fd = fopen(path, "r");
2249         if (!fd) return -2;
2250
2251         fseek(fd, 0L, SEEK_END);
2252         bytes = ftell(fd);
2253         rewind(fd);
2254
2255         if (progress_gauge_callback)
2256                 progress_gauge_callback(0, bytes);
2257
2258         while (offset < bytes) {
2259                 register size_t to_write;
2260
2261                 /* Read some data in */
2262                 to_write = fread(buf, 1, 4096, fd);
2263                 if (!to_write) {
2264                         if (feof(fd) || ferror(fd)) break;
2265                 }
2266                 sprintf(aaa, "WRIT %d", (int)to_write);
2267                 CtdlIPC_putline(ipc, aaa);
2268                 CtdlIPC_getline(ipc, aaa);
2269                 strcpy(cret, &aaa[4]);
2270                 ret = atoi(aaa);
2271                 if (aaa[0] == '7') {
2272                         to_write = extract_long(&aaa[4], 0);
2273                         
2274                         serv_write(ipc, buf, to_write);
2275                         offset += to_write;
2276                         if (progress_gauge_callback)
2277                                 progress_gauge_callback(offset, bytes);
2278                         /* Detect short reads and back up if needed */
2279                         /* offset will never be negative anyway */
2280                         fseek(fd, (signed)offset, SEEK_SET);
2281                 } else {
2282                         break;
2283                 }
2284         }
2285         if (progress_gauge_callback)
2286                 progress_gauge_callback(1, 1);
2287         return (!ferror(fd) ? ret : -2);
2288 }
2289
2290
2291 /*
2292  * Generic command method.  This method should handle any server command
2293  * except for CHAT.  It takes the following arguments:
2294  *
2295  * ipc                  The server to speak with
2296  * command              Preformatted command to send to server
2297  * to_send              A text or binary file to send to server
2298  *                      (only sent if server requests it)
2299  * bytes_to_send        The number of bytes in to_send (required if
2300  *                      sending binary, optional if sending listing)
2301  * to_receive           Pointer to a NULL pointer, if the server
2302  *                      sends text or binary we will allocate memory
2303  *                      for the file and stuff it here
2304  * bytes_to_receive     If a file is received, we will store its
2305  *                      byte count here
2306  * proto_response       The protocol response.  Caller must provide
2307  *                      this buffer and ensure that it is at least
2308  *                      128 bytes in length.
2309  *
2310  * This function returns a number equal to the protocol response number,
2311  * -1 if an internal error occurred, -2 if caller provided bad values,
2312  * or 0 - the protocol response number if bad values were found during
2313  * the protocol exchange.
2314  * It stores the protocol response string (minus the number) in 
2315  * protocol_response as described above.  Some commands send additional
2316  * data in this string.
2317  */
2318 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2319                 const char *command, const char *to_send,
2320                 size_t bytes_to_send, char **to_receive, 
2321                 size_t *bytes_to_receive, char *proto_response)
2322 {
2323         char buf[SIZ];
2324         register int ret;
2325         int watch_ssl = 0;
2326
2327         if (!command) return -2;
2328         if (!proto_response) return -2;
2329
2330 #ifdef HAVE_OPENSSL
2331         if (ipc->ssl) watch_ssl = 1;
2332 #endif
2333
2334         CtdlIPC_lock(ipc);
2335         CtdlIPC_putline(ipc, command);
2336         while (1) {
2337                 CtdlIPC_getline(ipc, proto_response);
2338                 if (proto_response[3] == '*')
2339                         express_msgs = 1;
2340                 ret = atoi(proto_response);
2341                 strcpy(proto_response, &proto_response[4]);
2342                 switch (ret / 100) {
2343                 default:                        /* Unknown, punt */
2344                 case 2:                         /* OK */
2345                 case 3:                         /* MORE_DATA */
2346                 case 5:                         /* ERROR */
2347                         /* Don't need to do anything */
2348                         break;
2349                 case 1:                         /* LISTING_FOLLOWS */
2350                         if (to_receive && !*to_receive && bytes_to_receive) {
2351                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2352                         } else { /* Drain */
2353                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2354                                 ret = -ret;
2355                         }
2356                         break;
2357                 case 4:                         /* SEND_LISTING */
2358                         if (to_send) {
2359                                 CtdlIPCSendListing(ipc, to_send);
2360                         } else {
2361                                 /* No listing given, fake it */
2362                                 CtdlIPC_putline(ipc, "000");
2363                                 ret = -ret;
2364                         }
2365                         break;
2366                 case 6:                         /* BINARY_FOLLOWS */
2367                         if (to_receive && !*to_receive && bytes_to_receive) {
2368                                 *bytes_to_receive =
2369                                         extract_long(proto_response, 0);
2370                                 *to_receive = (char *)
2371                                         malloc((size_t)*bytes_to_receive);
2372                                 if (!*to_receive) {
2373                                         ret = -1;
2374                                 } else {
2375                                         serv_read(ipc, *to_receive,
2376                                                         *bytes_to_receive);
2377                                 }
2378                         } else {
2379                                 /* Drain */
2380                                 size_t drain;
2381
2382                                 drain = extract_long(proto_response, 0);
2383                                 while (drain > SIZ) {
2384                                         serv_read(ipc, buf, SIZ);
2385                                         drain -= SIZ;
2386                                 }
2387                                 serv_read(ipc, buf, drain);
2388                                 ret = -ret;
2389                         }
2390                         break;
2391                 case 7:                         /* SEND_BINARY */
2392                         if (to_send && bytes_to_send) {
2393                                 serv_write(ipc, to_send, bytes_to_send);
2394                         } else if (bytes_to_send) {
2395                                 /* Fake it, send nulls */
2396                                 size_t fake;
2397
2398                                 fake = bytes_to_send;
2399                                 memset(buf, '\0', SIZ);
2400                                 while (fake > SIZ) {
2401                                         serv_write(ipc, buf, SIZ);
2402                                         fake -= SIZ;
2403                                 }
2404                                 serv_write(ipc, buf, fake);
2405                                 ret = -ret;
2406                         } /* else who knows?  DANGER WILL ROBINSON */
2407                         break;
2408                 case 8:                         /* START_CHAT_MODE */
2409                         if (!strncasecmp(command, "CHAT", 4)) {
2410                                 /* Don't call chatmode with generic! */
2411                                 CtdlIPC_putline(ipc, "/quit");
2412                                 ret = -ret;
2413                         } else {
2414                                 /* In this mode we send then receive listing */
2415                                 if (to_send) {
2416                                         CtdlIPCSendListing(ipc, to_send);
2417                                 } else {
2418                                         /* No listing given, fake it */
2419                                         CtdlIPC_putline(ipc, "000");
2420                                         ret = -ret;
2421                                 }
2422                                 if (to_receive && !*to_receive
2423                                                 && bytes_to_receive) {
2424                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2425                                 } else { /* Drain */
2426                                         while (CtdlIPC_getline(ipc, buf),
2427                                                         strcmp(buf, "000")) ;
2428                                         ret = -ret;
2429                                 }
2430                         }
2431                         break;
2432                 case 9:                         /* ASYNC_MSG */
2433                         /* CtdlIPCDoAsync(ret, proto_response); */
2434                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2435                         break;
2436                 }
2437                 if (ret / 100 != 9)
2438                         break;
2439         }
2440         CtdlIPC_unlock(ipc);
2441         return ret;
2442 }
2443
2444
2445 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2446 {
2447         struct hostent *phe;
2448         struct servent *pse;
2449         struct protoent *ppe;
2450         struct sockaddr_in sin;
2451         int s, type;
2452
2453         memset(&sin, 0, sizeof(sin));
2454         sin.sin_family = AF_INET;
2455
2456         pse = getservbyname(service, protocol);
2457         if (pse != NULL) {
2458                 sin.sin_port = pse->s_port;
2459         }
2460         else if (atoi(service) > 0) {
2461                 sin.sin_port = htons(atoi(service));
2462         }
2463         else {
2464                 sin.sin_port = htons(defaultPort);
2465         }
2466         phe = gethostbyname(host);
2467         if (phe) {
2468                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2469         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2470                 return -1;
2471         }
2472         if ((ppe = getprotobyname(protocol)) == 0) {
2473                 return -1;
2474         }
2475         if (!strcmp(protocol, "udp")) {
2476                 type = SOCK_DGRAM;
2477         } else {
2478                 type = SOCK_STREAM;
2479         }
2480
2481         s = socket(PF_INET, type, ppe->p_proto);
2482         if (s < 0) {
2483                 return -1;
2484         }
2485
2486         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2487                 return -1;
2488         }
2489
2490         return (s);
2491 }
2492
2493 static int uds_connectsock(int *isLocal, char *sockpath)
2494 {
2495         struct sockaddr_un addr;
2496         int s;
2497
2498         memset(&addr, 0, sizeof(addr));
2499         addr.sun_family = AF_UNIX;
2500         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2501
2502         s = socket(AF_UNIX, SOCK_STREAM, 0);
2503         if (s < 0) {
2504                 return -1;
2505         }
2506
2507         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2508                 return -1;
2509         }
2510
2511         *isLocal = 1;
2512         return s;
2513 }
2514
2515
2516 /*
2517  * input binary data from socket
2518  */
2519 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2520 {
2521         unsigned int len, rlen;
2522
2523 #if defined(HAVE_OPENSSL)
2524         if (ipc->ssl) {
2525                 serv_read_ssl(ipc, buf, bytes);
2526                 return;
2527         }
2528 #endif
2529         len = 0;
2530         while (len < bytes) {
2531                 rlen = read(ipc->sock, &buf[len], bytes - len);
2532                 if (rlen < 1) {
2533                         connection_died(ipc);
2534                         return;
2535                 }
2536                 len += rlen;
2537         }
2538 }
2539
2540
2541 /*
2542  * send binary to server
2543  */
2544 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2545 {
2546         unsigned int bytes_written = 0;
2547         int retval;
2548
2549 #if defined(HAVE_OPENSSL)
2550         if (ipc->ssl) {
2551                 serv_write_ssl(ipc, buf, nbytes);
2552                 return;
2553         }
2554 #endif
2555         while (bytes_written < nbytes) {
2556                 retval = write(ipc->sock, &buf[bytes_written],
2557                                nbytes - bytes_written);
2558                 if (retval < 1) {
2559                         connection_died(ipc);
2560                         return;
2561                 }
2562                 bytes_written += retval;
2563         }
2564 }
2565
2566
2567 #ifdef HAVE_OPENSSL
2568 /*
2569  * input binary data from encrypted connection
2570  */
2571 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2572 {
2573         int len, rlen;
2574         char junk[1];
2575
2576         len = 0;
2577         while (len < bytes) {
2578                 if (SSL_want_read(ipc->ssl)) {
2579                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2580                                 error_printf("SSL_write in serv_read:\n");
2581                                 ERR_print_errors_fp(stderr);
2582                         }
2583                 }
2584                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2585                 if (rlen < 1) {
2586                         long errval;
2587
2588                         errval = SSL_get_error(ipc->ssl, rlen);
2589                         if (errval == SSL_ERROR_WANT_READ ||
2590                                         errval == SSL_ERROR_WANT_WRITE) {
2591                                 sleep(1);
2592                                 continue;
2593                         }
2594                         if (errval == SSL_ERROR_ZERO_RETURN ||
2595                                         errval == SSL_ERROR_SSL) {
2596                                 serv_read(ipc, &buf[len], bytes - len);
2597                                 return;
2598                         }
2599                         error_printf("SSL_read in serv_read:\n");
2600                         ERR_print_errors_fp(stderr);
2601                         connection_died(NULL);
2602                         return;
2603                 }
2604                 len += rlen;
2605         }
2606 }
2607
2608
2609 /*
2610  * send binary to server encrypted
2611  */
2612 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2613 {
2614         unsigned int bytes_written = 0;
2615         int retval;
2616         char junk[1];
2617
2618         while (bytes_written < nbytes) {
2619                 if (SSL_want_write(ipc->ssl)) {
2620                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2621                                 error_printf("SSL_read in serv_write:\n");
2622                                 ERR_print_errors_fp(stderr);
2623                         }
2624                 }
2625                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2626                                 nbytes - bytes_written);
2627                 if (retval < 1) {
2628                         long errval;
2629
2630                         errval = SSL_get_error(ipc->ssl, retval);
2631                         if (errval == SSL_ERROR_WANT_READ ||
2632                                         errval == SSL_ERROR_WANT_WRITE) {
2633                                 sleep(1);
2634                                 continue;
2635                         }
2636                         if (errval == SSL_ERROR_ZERO_RETURN ||
2637                                         errval == SSL_ERROR_SSL) {
2638                                 serv_write(ipc, &buf[bytes_written],
2639                                                 nbytes - bytes_written);
2640                                 return;
2641                         }
2642                         error_printf("SSL_write in serv_write:\n");
2643                         ERR_print_errors_fp(stderr);
2644                         connection_died(NULL);
2645                         return;
2646                 }
2647                 bytes_written += retval;
2648         }
2649 }
2650
2651
2652 static void CtdlIPC_init_OpenSSL(void)
2653 {
2654         int a;
2655         SSL_METHOD *ssl_method;
2656         DH *dh;
2657         
2658         /* already done init */
2659         if (ssl_ctx) {
2660                 return;
2661         }
2662
2663         /* Get started */
2664         ssl_ctx = NULL;
2665         dh = NULL;
2666         SSL_load_error_strings();
2667         SSLeay_add_ssl_algorithms();
2668
2669         /* Set up the SSL context in which we will oeprate */
2670         ssl_method = SSLv23_client_method();
2671         ssl_ctx = SSL_CTX_new(ssl_method);
2672         if (!ssl_ctx) {
2673                 error_printf("SSL_CTX_new failed: %s\n",
2674                                 ERR_reason_error_string(ERR_get_error()));
2675                 return;
2676         }
2677         /* Any reasonable cipher we can get */
2678         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2679                 error_printf("No ciphers available for encryption\n");
2680                 return;
2681         }
2682         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2683         
2684         /* Load DH parameters into the context */
2685         dh = DH_new();
2686         if (!dh) {
2687                 error_printf("Can't allocate a DH object: %s\n",
2688                                 ERR_reason_error_string(ERR_get_error()));
2689                 return;
2690         }
2691         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2692                 error_printf("Can't assign DH_P: %s\n",
2693                                 ERR_reason_error_string(ERR_get_error()));
2694                 DH_free(dh);
2695                 return;
2696         }
2697         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2698                 error_printf("Can't assign DH_G: %s\n",
2699                                 ERR_reason_error_string(ERR_get_error()));
2700                 DH_free(dh);
2701                 return;
2702         }
2703         dh->length = DH_L;
2704         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2705         DH_free(dh);
2706
2707 #ifdef THREADED_CLIENT
2708         /* OpenSSL requires callbacks for threaded clients */
2709         CRYPTO_set_locking_callback(ssl_lock);
2710         CRYPTO_set_id_callback(id_callback);
2711
2712         /* OpenSSL requires us to do semaphores for threaded clients */
2713         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2714         if (!Critters) {
2715                 perror("malloc failed");
2716                 exit(1);
2717         } else {
2718                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2719                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2720                         if (!Critters[a]) {
2721                                 perror("malloc failed");
2722                                 exit(1);
2723                         }
2724                         pthread_mutex_init(Critters[a], NULL);
2725                 }
2726         }
2727 #endif /* THREADED_CLIENT */       
2728 }
2729
2730
2731 static void ssl_lock(int mode, int n, const char *file, int line)
2732 {
2733 #ifdef THREADED_CLIENT
2734         if (mode & CRYPTO_LOCK)
2735                 pthread_mutex_lock(Critters[n]);
2736         else
2737                 pthread_mutex_unlock(Critters[n]);
2738 #endif /* THREADED_CLIENT */
2739 }
2740
2741 #ifdef THREADED_CLIENT
2742 static unsigned long id_callback(void) {
2743         return (unsigned long)pthread_self();
2744 }
2745 #endif /* THREADED_CLIENT */
2746 #endif /* HAVE_OPENSSL */
2747
2748
2749 /*
2750  * input string from socket - implemented in terms of serv_read()
2751  */
2752 void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2753 {
2754         int i;
2755
2756         /* Read one character at a time. */
2757         for (i = 0;; i++) {
2758                 serv_read(ipc, &buf[i], 1);
2759                 if (buf[i] == '\n' || i == (SIZ-1))
2760                         break;
2761         }
2762
2763         /* If we got a long line, discard characters until the newline. */
2764         if (i == (SIZ-1))
2765                 while (buf[i] != '\n')
2766                         serv_read(ipc, &buf[i], 1);
2767
2768         /* Strip the trailing newline.
2769          */
2770         buf[i] = 0;
2771 }
2772
2773
2774 /*
2775  * send line to server - implemented in terms of serv_write()
2776  */
2777 void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2778 {
2779         /* error_printf("< %s\n", buf); */
2780         serv_write(ipc, buf, strlen(buf));
2781         serv_write(ipc, "\n", 1);
2782
2783         ipc->last_command_sent = time(NULL);
2784 }
2785
2786
2787 /*
2788  * attach to server
2789  */
2790 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2791 {
2792         int a;
2793         char cithost[SIZ];
2794         char citport[SIZ];
2795         char sockpath[SIZ];
2796
2797         CtdlIPC *ipc = ialloc(CtdlIPC);
2798         if (!ipc) {
2799                 return 0;
2800         }
2801 #if defined(HAVE_OPENSSL)
2802         ipc->ssl = NULL;
2803         CtdlIPC_init_OpenSSL();
2804 #endif
2805 #if defined(HAVE_PTHREAD_H)
2806         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2807 #endif
2808         ipc->sock = -1;                 /* Not connected */
2809         ipc->isLocal = 0;               /* Not local, of course! */
2810         ipc->downloading = 0;
2811         ipc->uploading = 0;
2812         ipc->last_command_sent = 0L;
2813         ipc->network_status_cb = NULL;
2814
2815         strcpy(cithost, DEFAULT_HOST);  /* default host */
2816         strcpy(citport, DEFAULT_PORT);  /* default port */
2817
2818         for (a = 0; a < argc; ++a) {
2819                 if (a == 0) {
2820                         /* do nothing */
2821                 } else if (a == 1) {
2822                         strcpy(cithost, argv[a]);
2823                 } else if (a == 2) {
2824                         strcpy(citport, argv[a]);
2825                 } else {
2826                         error_printf("%s: usage: ",argv[0]);
2827                         error_printf("%s [host] [port] ",argv[0]);
2828                         ifree(ipc);
2829                         errno = EINVAL;
2830                         return 0;
2831                 }
2832         }
2833
2834         if ((!strcmp(cithost, "localhost"))
2835            || (!strcmp(cithost, "127.0.0.1"))) {
2836                 ipc->isLocal = 1;
2837         }
2838
2839         /* If we're using a unix domain socket we can do a bunch of stuff */
2840         if (!strcmp(cithost, UDS)) {
2841                 snprintf(sockpath, sizeof sockpath, BBSDIR "/citadel.socket");
2842                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2843                 if (ipc->sock == -1) {
2844                         ifree(ipc);
2845                         return 0;
2846                 }
2847                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2848                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2849                 return ipc;
2850         }
2851
2852         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2853         if (ipc->sock == -1) {
2854                 ifree(ipc);
2855                 return 0;
2856         }
2857         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2858         if (portbuf != NULL) strcpy(portbuf, citport);
2859         return ipc;
2860 }
2861
2862 /*
2863  * return the file descriptor of the server socket so we can select() on it.
2864  *
2865  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2866  * rewritten...
2867  */
2868 int CtdlIPC_getsockfd(CtdlIPC* ipc)
2869 {
2870         return ipc->sock;
2871 }
2872
2873
2874 /*
2875  * return one character
2876  *
2877  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2878  * rewritten...
2879  */
2880 char CtdlIPC_get(CtdlIPC* ipc)
2881 {
2882         char buf[2];
2883         char ch;
2884
2885         serv_read(ipc, buf, 1);
2886         ch = (int) buf[0];
2887
2888         return (ch);
2889 }