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