source: functions.c @ 5b75e8b

debianrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 5b75e8b was 57cf4f9, checked in by Nelson Elhage <nelhage@mit.edu>, 16 years ago
Make owl_function_reply call methods to get the replycmd. For backwards compatibility, we add default sub replycmd and sub replysendercmd to BarnOwl::Message, which just return the value of the property.
  • Property mode set to 100644
File size: 100.0 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
5#include <netinet/in.h>
6#include <string.h>
7#include <time.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/wait.h>
11#include <errno.h>
12#include <signal.h>
13#include "owl.h"
14
15static const char fileIdent[] = "$Id$";
16
17void owl_function_noop(void)
18{
19  return;
20}
21
22char *owl_function_command(char *cmdbuff)
23{
24  owl_function_debugmsg("executing command: %s", cmdbuff);
25  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
26                             owl_global_get_context(&g), cmdbuff);
27}
28
29char *owl_function_command_argv(char **argv, int argc)
30{
31  return owl_cmddict_execute_argv(owl_global_get_cmddict(&g),
32                                  owl_global_get_context(&g),
33                                  argv, argc);
34}
35
36void owl_function_command_norv(char *cmdbuff)
37{
38  char *rv;
39  rv=owl_function_command(cmdbuff);
40  if (rv) owl_free(rv);
41}
42
43void owl_function_command_alias(char *alias_from, char *alias_to)
44{
45  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
46}
47
48owl_cmd *owl_function_get_cmd(char *name)
49{
50  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
51}
52
53void owl_function_show_commands()
54{
55  owl_list l;
56  owl_fmtext fm;
57
58  owl_fmtext_init_null(&fm);
59  owl_fmtext_append_bold(&fm, "Commands:  ");
60  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
61  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
62  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
63  owl_fmtext_append_normal(&fm, "\n");
64  owl_function_popless_fmtext(&fm);
65  owl_cmddict_namelist_free(&l);
66  owl_fmtext_free(&fm);
67}
68
69void owl_function_show_view(char *viewname)
70{
71  owl_view *v;
72  owl_fmtext fm;
73
74  /* we only have the one view right now */
75  v=owl_global_get_current_view(&g);
76  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
77    owl_function_error("No view named '%s'", viewname);
78    return;
79  }
80
81  owl_fmtext_init_null(&fm);
82  owl_view_to_fmtext(v, &fm);
83  owl_function_popless_fmtext(&fm);
84  owl_fmtext_free(&fm);
85}
86
87void owl_function_show_styles() {
88  owl_list l;
89  owl_fmtext fm;
90
91  owl_fmtext_init_null(&fm);
92  owl_fmtext_append_bold(&fm, "Styles:\n");
93  owl_global_get_style_names(&g, &l);
94  owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe);
95  owl_fmtext_append_normal(&fm, "\n");
96  owl_function_popless_fmtext(&fm);
97  owl_list_free_all(&l, owl_free);
98  owl_fmtext_free(&fm);
99}
100
101char *owl_function_style_describe(void *name) {
102  char *desc, *s;
103  owl_style *style;
104  style = owl_global_get_style_by_name(&g, name);
105  if (style) {
106    desc = owl_style_get_description(style);
107  } else {
108    desc = "???";
109  }
110  s = owl_sprintf("%-20s - %s%s", name, 
111                  0==owl_style_validate(style)?"":"[INVALID] ",
112                  desc);
113  return s;
114}
115
116char *owl_function_cmd_describe(void *name)
117{
118  owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
119  if (cmd) return owl_cmd_describe(cmd);
120  else return(NULL);
121}
122
123void owl_function_show_command(char *name)
124{
125  owl_function_help_for_command(name);
126}
127
128void owl_function_show_license()
129{
130  char *text;
131
132  text=""
133    "barnowl version " OWL_VERSION_STRING "\n"
134    "Copyright (c) 2006-2008 The BarnOwl Developers. All rights reserved.\n"
135    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
136    "\n"
137    "Redistribution and use in source and binary forms, with or without\n"
138    "modification, are permitted provided that the following conditions are\n"
139    "met:\n"
140    "\n"
141    "   * Redistributions of source code must retain the above copyright\n"
142    "     notice, this list of conditions and the following disclaimer.\n"
143    "\n"
144    "   * Redistributions in binary form must reproduce the above copyright\n"
145    "     notice, this list of conditions and the following disclaimer in\n"
146    "     the documentation and/or other materials provided with the\n"
147    "     distribution.\n"
148    "\n"
149    "   * Redistributions in any form must be accompanied by information on\n"
150    "     how to obtain complete source code for the Owl software and any\n"
151    "     accompanying software that uses the Owl software. The source code\n"
152    "     must either be included in the distribution or be available for no\n"
153    "     more than the cost of distribution plus a nominal fee, and must be\n"
154    "     freely redistributable under reasonable conditions. For an\n"
155    "     executable file, complete source code means the source code for\n"
156    "     all modules it contains. It does not include source code for\n"
157    "     modules or files that typically accompany the major components of\n"
158    "     the operating system on which the executable file runs.\n"
159    "\n"
160    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
161    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
162    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
163    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
164    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
165    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
166    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
167    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
168    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
169    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
170    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
171  owl_function_popless_text(text);
172}
173
174/* Create an admin message, append it to the global list of messages
175 * and redisplay if necessary.
176 */
177void owl_function_adminmsg(char *header, char *body)
178{
179  owl_message *m;
180
181  m=owl_malloc(sizeof(owl_message));
182  owl_message_create_admin(m, header, body);
183 
184  /* add it to the global list and current view */
185  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
186  owl_view_consider_message(owl_global_get_current_view(&g), m);
187
188  /* do followlast if necessary */
189  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
190
191  /* redisplay etc. */
192  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
193  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
194    owl_popwin_refresh(owl_global_get_popwin(&g));
195  }
196  wnoutrefresh(owl_global_get_curs_recwin(&g));
197  owl_global_set_needrefresh(&g);
198}
199
200/* Create an outgoing zephyr message and return a pointer to it.  Does
201 * not put it on the global queue, use owl_function_add_message() for
202 * that.
203 */
204owl_message *owl_function_make_outgoing_zephyr(char *body, char *zwriteline, char *zsig)
205{
206  owl_message *m;
207  owl_zwrite z;
208 
209  /* create a zwrite for the purpose of filling in other message fields */
210  owl_zwrite_create_from_line(&z, zwriteline);
211
212  /* create the message */
213  m=owl_malloc(sizeof(owl_message));
214  owl_message_create_from_zwriteline(m, zwriteline, body, zsig);
215  owl_zwrite_free(&z);
216
217  return(m);
218}
219
220/* Create an outgoing AIM message, returns a pointer to the created
221 * message or NULL if we're not logged into AIM (and thus unable to
222 * create the message).  Does not put it on the global queue.  Use
223 * owl_function_add_message() for that .
224 */
225owl_message *owl_function_make_outgoing_aim(char *body, char *to)
226{
227  owl_message *m;
228
229  /* error if we're not logged into aim */
230  if (!owl_global_is_aimloggedin(&g)) return(NULL);
231 
232  m=owl_malloc(sizeof(owl_message));
233  owl_message_create_aim(m,
234                         owl_global_get_aim_screenname(&g),
235                         to,
236                         body,
237                         OWL_MESSAGE_DIRECTION_OUT,
238                         0);
239  return(m);
240}
241
242/* Create an outgoing loopback message and return a pointer to it.
243 * Does not append it to the global queue, use
244 * owl_function_add_message() for that.
245 */
246owl_message *owl_function_make_outgoing_loopback(char *body)
247{
248  owl_message *m;
249
250  /* create the message */
251  m=owl_malloc(sizeof(owl_message));
252  owl_message_create_loopback(m, body);
253  owl_message_set_direction_out(m);
254
255  return(m);
256}
257
258void owl_function_zwrite_setup(char *line)
259{
260  owl_editwin *e;
261  char buff[1024];
262  owl_zwrite z;
263  int ret;
264
265  /* check the arguments */
266  ret=owl_zwrite_create_from_line(&z, line);
267  if (ret) {
268    owl_function_error("Error in zwrite arguments");
269    owl_zwrite_free(&z);
270    return;
271  }
272
273  /* send a ping if necessary */
274  if (owl_global_is_txping(&g)) {
275    owl_zwrite_send_ping(&z);
276  }
277  owl_zwrite_free(&z);
278
279  /* create and setup the editwin */
280  e=owl_global_get_typwin(&g);
281  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
282
283  if (!owl_global_get_lockout_ctrld(&g)) {
284    owl_function_makemsg("Type your zephyr below.  End with ^D or a dot on a line by itself.  ^C will quit.");
285  } else {
286    owl_function_makemsg("Type your zephyr below.  End with a dot on a line by itself.  ^C will quit.");
287  }
288
289  owl_editwin_clear(e);
290  owl_editwin_set_dotsend(e);
291  strcpy(buff, "----> ");
292  strncat(buff, line, 1016);
293  strcat(buff, "\n");
294  owl_editwin_set_locktext(e, buff);
295
296  /* make it active */
297  owl_global_set_typwin_active(&g);
298
299  owl_global_set_buffercommand(&g, line);
300  owl_global_set_buffercallback(&g, &owl_callback_zwrite);
301}
302
303void owl_function_aimwrite_setup(char *line)
304{
305  owl_editwin *e;
306  char buff[1024];
307
308  /* check the arguments */
309
310  /* create and setup the editwin */
311  e=owl_global_get_typwin(&g);
312  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
313
314  if (!owl_global_get_lockout_ctrld(&g)) {
315    owl_function_makemsg("Type your message below.  End with ^D or a dot on a line by itself.  ^C will quit.");
316  } else {
317    owl_function_makemsg("Type your message below.  End with a dot on a line by itself.  ^C will quit.");
318  }
319
320  owl_editwin_clear(e);
321  owl_editwin_set_dotsend(e);
322  strcpy(buff, "----> ");
323  strcat(buff, line);
324  strcat(buff, "\n");
325  owl_editwin_set_locktext(e, buff);
326
327  /* make it active */
328  owl_global_set_typwin_active(&g);
329
330  owl_global_set_buffercommand(&g, line);
331  owl_global_set_buffercallback(&g, &owl_callback_aimwrite);
332}
333
334void owl_function_loopwrite_setup()
335{
336  owl_editwin *e;
337
338  /* create and setup the editwin */
339  e=owl_global_get_typwin(&g);
340  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
341
342  if (!owl_global_get_lockout_ctrld(&g)) {
343    owl_function_makemsg("Type your message below.  End with ^D or a dot on a line by itself.  ^C will quit.");
344  } else {
345    owl_function_makemsg("Type your message below.  End with a dot on a line by itself.  ^C will quit.");
346  }
347
348  owl_editwin_clear(e);
349  owl_editwin_set_dotsend(e);
350  owl_editwin_set_locktext(e, "----> loopwrite\n");
351
352  /* make it active */
353  owl_global_set_typwin_active(&g);
354
355  owl_global_set_buffercommand(&g, "loopwrite");
356  owl_global_set_buffercallback(&g, &owl_callback_loopwrite);
357}
358
359void owl_callback_zwrite(owl_editwin *e) {
360  owl_function_zwrite(owl_editwin_get_command(e),
361                      owl_editwin_get_text(e));
362}
363
364/* send, log and display an outgoing zephyr.  If 'msg' is NULL
365 * the message is expected to be set from the zwrite line itself
366 */
367void owl_function_zwrite(char *line, char *msg)
368{
369  owl_zwrite z;
370  char *mymsg;
371  owl_message *m;
372
373  if(!strncmp(line, "zcrypt", strlen("zcrypt"))) {
374    owl_function_zcrypt(line, msg);
375    return;
376  }
377
378  /* create the zwrite and send the message */
379  owl_zwrite_create_from_line(&z, line);
380  if (msg) {
381    owl_zwrite_set_message(&z, msg);
382  }
383  owl_zwrite_send_message(&z);
384  owl_function_makemsg("Waiting for ack...");
385
386  /* If it's personal */
387  if (owl_zwrite_is_personal(&z)) {
388    /* create the outgoing message */
389    mymsg=owl_zwrite_get_message(&z);
390    m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z));
391
392    if (m) {
393      owl_global_messagequeue_addmsg(&g, m);
394    } else {
395      owl_function_error("Could not create outgoing zephyr message");
396    }
397  }
398
399  /* free the zwrite */
400  owl_zwrite_free(&z);
401}
402
403/* send, log and display an outgoing zcrypt zephyr.  If 'msg' is NULL
404 * the message is expected to be set from the zwrite line itself
405 */
406void owl_function_zcrypt(char *line, char *msg)
407{
408  owl_zwrite z;
409  char *mymsg;
410  char *cryptmsg;
411  owl_message *m;
412#ifdef OWL_ENABLE_ZCRYPT
413  int ret;
414#endif
415
416  /* create the zwrite and send the message */
417  owl_zwrite_create_from_line(&z, line);
418  if (msg) {
419    owl_zwrite_set_message(&z, msg);
420  }
421
422  mymsg=owl_zwrite_get_message(&z);
423#ifdef OWL_ENABLE_ZCRYPT
424  cryptmsg=owl_malloc(strlen(mymsg)*4);
425  ret=owl_zcrypt_encrypt(cryptmsg, mymsg, owl_zwrite_get_class(&z), owl_zwrite_get_instance(&z));
426  if (ret) {
427    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
428    owl_function_beep();
429    owl_free(cryptmsg);
430    return;
431  }
432#else
433  cryptmsg=owl_strdup(mymsg);
434#endif
435
436  owl_zwrite_set_message(&z, cryptmsg);
437  owl_zwrite_set_opcode(&z, "crypt");
438  mymsg=cryptmsg;
439   
440  owl_zwrite_send_message(&z);
441  owl_function_makemsg("Waiting for ack...");
442
443  /* If it's personal */
444  if (owl_zwrite_is_personal(&z)) {
445    /* create the outgoing message */
446    mymsg=owl_zwrite_get_message(&z);
447    m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z));
448    if (m) {
449      owl_global_messagequeue_addmsg(&g, m);
450    } else {
451      owl_function_error("Could not create outgoing zephyr message");
452    }
453  }
454
455  /* free the zwrite */
456  owl_free(cryptmsg);
457  owl_zwrite_free(&z);
458}
459
460void owl_callback_aimwrite(owl_editwin *e) {
461  owl_function_aimwrite(owl_editwin_get_command(e),
462                        owl_editwin_get_text(e));
463}
464
465void owl_function_aimwrite(char *line, char *msg)
466{
467  int ret;
468  char *to, *format_msg;
469  owl_message *m;
470
471  to = line + 9;
472
473  /* make a formatted copy of the message */
474  format_msg=owl_strdup(msg);
475  owl_text_wordunwrap(format_msg);
476 
477  /* send the message */
478  ret=owl_aim_send_im(to, format_msg);
479  if (!ret) {
480    owl_function_makemsg("AIM message sent.");
481  } else {
482    owl_function_error("Could not send AIM message.");
483  }
484
485  /* create the outgoing message */
486  m=owl_function_make_outgoing_aim(msg, to);
487
488  if (m) {
489    owl_global_messagequeue_addmsg(&g, m);
490  } else {
491    owl_function_error("Could not create outgoing AIM message");
492  }
493
494  owl_free(format_msg);
495}
496
497void owl_function_send_aimawymsg(char *to, char *msg)
498{
499  int ret;
500  char *format_msg;
501  owl_message *m;
502
503  /* make a formatted copy of the message */
504  format_msg=owl_strdup(msg);
505  owl_text_wordunwrap(format_msg);
506 
507  /* send the message */
508  ret=owl_aim_send_awaymsg(to, format_msg);
509  if (!ret) {
510    /* owl_function_makemsg("AIM message sent."); */
511  } else {
512    owl_function_error("Could not send AIM message.");
513  }
514
515  /* create the message */
516  m=owl_function_make_outgoing_aim(msg, to);
517  if (m) {
518    owl_global_messagequeue_addmsg(&g, m);
519  } else {
520    owl_function_error("Could not create AIM message");
521  }
522  owl_free(format_msg);
523}
524
525void owl_callback_loopwrite(owl_editwin *e) {
526  owl_function_loopwrite(owl_editwin_get_text(e));
527}
528
529void owl_function_loopwrite(char *msg)
530{
531  owl_message *min, *mout;
532
533  /* create a message and put it on the message queue.  This simulates
534   * an incoming message */
535  min=owl_malloc(sizeof(owl_message));
536  mout=owl_function_make_outgoing_loopback(msg);
537
538  if (owl_global_is_displayoutgoing(&g)) {
539    owl_global_messagequeue_addmsg(&g, mout);
540  } else {
541    owl_message_free(mout);
542  }
543
544  owl_message_create_loopback(min, msg);
545  owl_message_set_direction_in(min);
546  owl_global_messagequeue_addmsg(&g, min);
547
548  /* fake a makemsg */
549  owl_function_makemsg("loopback message sent");
550}
551
552/* If filter is non-null, looks for the next message matching
553 * that filter.  If skip_deleted, skips any deleted messages.
554 * If last_if_none, will stop at the last message in the view
555 * if no matching messages are found.  */
556void owl_function_nextmsg_full(char *filter, int skip_deleted, int last_if_none)
557{
558  int curmsg, i, viewsize, found;
559  owl_view *v;
560  owl_filter *f = NULL;
561  owl_message *m;
562
563  v=owl_global_get_current_view(&g);
564
565  if (filter) {
566    f=owl_global_get_filter(&g, filter);
567    if (!f) {
568      owl_function_error("No %s filter defined", filter);
569      return;
570    }
571  }
572
573  curmsg=owl_global_get_curmsg(&g);
574  viewsize=owl_view_get_size(v);
575  found=0;
576
577  /* just check to make sure we're in bounds... */
578  if (curmsg>viewsize-1) curmsg=viewsize-1;
579  if (curmsg<0) curmsg=0;
580
581  for (i=curmsg+1; i<viewsize; i++) {
582    m=owl_view_get_element(v, i);
583    if (skip_deleted && owl_message_is_delete(m)) continue;
584    if (f && !owl_filter_message_match(f, m)) continue;
585    found = 1;
586    break;
587  }
588
589  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
590  if (i<0) i=0;
591
592  if (!found) {
593    owl_function_makemsg("already at last%s message%s%s",
594                         skip_deleted?" non-deleted":"",
595                         filter?" in ":"", filter?filter:"");
596    /* if (!skip_deleted) owl_function_beep(); */
597  }
598
599  if (last_if_none || found) {
600    owl_global_set_curmsg(&g, i);
601    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
602    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
603    owl_global_set_direction_downwards(&g);
604  }
605}
606
607void owl_function_prevmsg_full(char *filter, int skip_deleted, int first_if_none)
608{
609  int curmsg, i, viewsize, found;
610  owl_view *v;
611  owl_filter *f = NULL;
612  owl_message *m;
613
614  v=owl_global_get_current_view(&g);
615
616  if (filter) {
617    f=owl_global_get_filter(&g, filter);
618    if (!f) {
619      owl_function_error("No %s filter defined", filter);
620      return;
621    }
622  }
623
624  curmsg=owl_global_get_curmsg(&g);
625  viewsize=owl_view_get_size(v);
626  found=0;
627
628  /* just check to make sure we're in bounds... */
629  if (curmsg<0) curmsg=0;
630
631  for (i=curmsg-1; i>=0; i--) {
632    m=owl_view_get_element(v, i);
633    if (skip_deleted && owl_message_is_delete(m)) continue;
634    if (f && !owl_filter_message_match(f, m)) continue;
635    found = 1;
636    break;
637  }
638
639  if (i<0) i=0;
640
641  if (!found) {
642    owl_function_makemsg("already at first%s message%s%s",
643                         skip_deleted?" non-deleted":"",
644                         filter?" in ":"", filter?filter:"");
645    /* if (!skip_deleted) owl_function_beep(); */
646  }
647
648  if (first_if_none || found) {
649    owl_global_set_curmsg(&g, i);
650    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
651    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
652    owl_global_set_direction_upwards(&g);
653  }
654}
655
656void owl_function_nextmsg()
657{
658  owl_function_nextmsg_full(NULL, 0, 1);
659}
660
661void owl_function_prevmsg()
662{
663  owl_function_prevmsg_full(NULL, 0, 1);
664}
665
666void owl_function_nextmsg_notdeleted()
667{
668  owl_function_nextmsg_full(NULL, 1, 1);
669}
670
671void owl_function_prevmsg_notdeleted()
672{
673  owl_function_prevmsg_full(NULL, 1, 1);
674}
675
676void owl_function_nextmsg_personal()
677{
678  owl_function_nextmsg_full("personal", 0, 0);
679}
680
681void owl_function_prevmsg_personal()
682{
683  owl_function_prevmsg_full("personal", 0, 0);
684}
685
686
687/* if move_after is 1, moves after the delete */
688void owl_function_deletecur(int move_after)
689{
690  int curmsg;
691  owl_view *v;
692
693  v=owl_global_get_current_view(&g);
694
695  /* bail if there's no current message */
696  if (owl_view_get_size(v) < 1) {
697    owl_function_error("No current message to delete");
698    return;
699  }
700
701  /* mark the message for deletion */
702  curmsg=owl_global_get_curmsg(&g);
703  owl_view_delete_element(v, curmsg);
704
705  if (move_after) {
706    /* move the poiner in the appropriate direction
707     * to the next undeleted msg */
708    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
709      owl_function_prevmsg_notdeleted();
710    } else {
711      owl_function_nextmsg_notdeleted();
712    }
713  }
714}
715
716void owl_function_undeletecur(int move_after)
717{
718  int curmsg;
719  owl_view *v;
720
721  v=owl_global_get_current_view(&g);
722 
723  if (owl_view_get_size(v) < 1) {
724    owl_function_error("No current message to undelete");
725    return;
726  }
727  curmsg=owl_global_get_curmsg(&g);
728
729  owl_view_undelete_element(v, curmsg);
730
731  if (move_after) {
732    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
733      if (curmsg>0) {
734        owl_function_prevmsg();
735      } else {
736        owl_function_nextmsg();
737      }
738    } else {
739      owl_function_nextmsg();
740    }
741  }
742
743  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
744}
745
746void owl_function_expunge()
747{
748  int curmsg;
749  owl_message *m;
750  owl_messagelist *ml;
751  owl_view *v;
752  int lastmsgid=0;
753
754  curmsg=owl_global_get_curmsg(&g);
755  v=owl_global_get_current_view(&g);
756  ml=owl_global_get_msglist(&g);
757
758  m=owl_view_get_element(v, curmsg);
759  if (m) lastmsgid = owl_message_get_id(m);
760
761  /* expunge the message list */
762  owl_messagelist_expunge(ml);
763
764  /* update all views (we only have one right now) */
765  owl_view_recalculate(v);
766
767  /* find where the new position should be
768     (as close as possible to where we last where) */
769  curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid);
770  if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1;
771  if (curmsg<0) curmsg = 0;
772  owl_global_set_curmsg(&g, curmsg);
773  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
774  /* if there are no messages set the direction to down in case we
775     delete everything upwards */
776  owl_global_set_direction_downwards(&g);
777 
778  owl_function_makemsg("Messages expunged");
779  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
780}
781
782void owl_function_firstmsg()
783{
784  owl_global_set_curmsg(&g, 0);
785  owl_global_set_topmsg(&g, 0);
786  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
787  owl_global_set_direction_downwards(&g);
788}
789
790void owl_function_lastmsg_noredisplay()
791{
792  int oldcurmsg, curmsg;
793  owl_view *v;
794
795  v=owl_global_get_current_view(&g);
796  oldcurmsg=owl_global_get_curmsg(&g);
797  curmsg=owl_view_get_size(v)-1; 
798  if (curmsg<0) curmsg=0;
799  owl_global_set_curmsg(&g, curmsg);
800  if (oldcurmsg < curmsg) {
801    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
802  } else if (curmsg<owl_view_get_size(v)) {
803    /* If already at the end, blank the screen and move curmsg
804     * past the end of the messages. */
805    owl_global_set_topmsg(&g, curmsg+1);
806    owl_global_set_curmsg(&g, curmsg+1);
807  } 
808  /* owl_mainwin_redisplay(owl_global_get_mainwin(&g)); */
809  owl_global_set_direction_downwards(&g);
810}
811
812void owl_function_lastmsg()
813{
814  owl_function_lastmsg_noredisplay();
815  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
816}
817
818void owl_function_shift_right()
819{
820  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
821  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
822  owl_global_set_needrefresh(&g);
823}
824
825void owl_function_shift_left()
826{
827  int shift;
828
829  shift=owl_global_get_rightshift(&g);
830  if (shift>=10) {
831    owl_global_set_rightshift(&g, shift-10);
832    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
833    owl_global_set_needrefresh(&g);
834  } else {
835    owl_function_beep();
836    owl_function_makemsg("Already full left");
837  }
838}
839
840void owl_function_unsuball()
841{
842  unsuball();
843  owl_function_makemsg("Unsubscribed from all messages.");
844}
845
846
847/* Load zephyr subscriptions from the named 'file' and load zephyr's
848 * default subscriptions as well.  An error message is printed if
849 * 'file' can't be opened or if zephyr reports an error in
850 * subscribing.
851 *
852 * If 'file' is NULL, this look for the default filename
853 * $HOME/.zephyr.subs.  If the file can not be opened in this case
854 * only, no error message is printed.
855 */
856void owl_function_loadsubs(char *file)
857{
858  int ret, ret2;
859  char *foo, *path;
860
861  if (file==NULL) {
862    ret=owl_zephyr_loadsubs(NULL, 0);
863  } else {
864    path = owl_util_makepath(file);
865    ret=owl_zephyr_loadsubs(path, 1);
866    free(path);
867  }
868
869  /* for backwards compatibility for now */
870  ret2=owl_zephyr_loaddefaultsubs();
871
872  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
873
874  foo=file?file:"file";
875  if (ret==0 && ret2==0) {
876    if (!file) {
877      owl_function_makemsg("Subscribed to messages.");
878    } else {
879      owl_function_makemsg("Subscribed to messages from %s", file);
880    }
881  } else if (ret==-1) {
882    owl_function_error("Could not read %s", foo);
883  } else {
884    owl_function_error("Error subscribing to messages");
885  }
886}
887
888void owl_function_loadloginsubs(char *file)
889{
890  int ret;
891
892  ret=owl_zephyr_loadloginsubs(file);
893
894  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
895  if (ret==0) {
896    owl_function_makemsg("Subscribed to login messages from file.");
897  } else if (ret==-1) {
898    owl_function_error("Could not open file for login subscriptions.");
899  } else {
900    owl_function_error("Error subscribing to login messages from file.");
901  }
902}
903
904void owl_callback_aimlogin(owl_editwin *e) {
905  owl_function_aimlogin(owl_editwin_get_command(e),
906                        owl_editwin_get_text(e));
907}
908
909void owl_function_aimlogin(char *user, char *passwd) {
910  int ret;
911
912  /* clear the buddylist */
913  owl_buddylist_clear(owl_global_get_buddylist(&g));
914
915  /* try to login */
916  ret=owl_aim_login(user, passwd);
917  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
918}
919
920void owl_function_suspend()
921{
922  endwin();
923  printf("\n");
924  kill(getpid(), SIGSTOP);
925
926  /* resize to reinitialize all the windows when we come back */
927  owl_command_resize();
928}
929
930void owl_function_zaway_toggle()
931{
932  if (!owl_global_is_zaway(&g)) {
933    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
934    owl_function_zaway_on();
935  } else {
936    owl_function_zaway_off();
937  }
938}
939
940void owl_function_zaway_on()
941{
942  owl_global_set_zaway_on(&g);
943  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
944}
945
946void owl_function_zaway_off()
947{
948  owl_global_set_zaway_off(&g);
949  owl_function_makemsg("zaway off");
950}
951
952void owl_function_aaway_toggle()
953{
954  if (!owl_global_is_aaway(&g)) {
955    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
956    owl_function_aaway_on();
957  } else {
958    owl_function_aaway_off();
959  }
960}
961
962void owl_function_aaway_on()
963{
964  owl_global_set_aaway_on(&g);
965  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
966  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
967}
968
969void owl_function_aaway_off()
970{
971  owl_global_set_aaway_off(&g);
972  /* owl_aim_set_awaymsg(""); */
973  owl_function_makemsg("AIM away off");
974}
975
976void owl_function_quit()
977{
978  char *ret;
979 
980  /* zlog out if we need to */
981  if (owl_global_is_shutdownlogout(&g)) {
982    owl_zephyr_zlog_out();
983  }
984
985  /* execute the commands in shutdown */
986  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
987  if (ret) owl_free(ret);
988
989  /* signal our child process, if any */
990  if (owl_global_get_newmsgproc_pid(&g)) {
991    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
992  }
993
994  /* Quit zephyr */
995  owl_zephyr_shutdown();
996 
997  /* Quit AIM */
998  if (owl_global_is_aimloggedin(&g)) {
999    owl_aim_logout();
1000  }
1001
1002  /* done with curses */
1003  endwin();
1004
1005  /* restore terminal settings */
1006  tcsetattr(0, TCSAFLUSH, owl_global_get_startup_tio(&g));
1007
1008  owl_function_debugmsg("Quitting Owl");
1009  exit(0);
1010}
1011
1012void owl_function_openurl()
1013{
1014  /* visit the first url in the current message */
1015  owl_message *m;
1016  owl_view *v;
1017  char *ptr1, *ptr2, *text, url[LINE], tmpbuff[LINE];
1018  int webbrowser;
1019
1020  webbrowser = owl_global_get_webbrowser(&g);
1021
1022  if (webbrowser < 0 || webbrowser == OWL_WEBBROWSER_NONE) {
1023    owl_function_error("No browser selected");
1024    return;
1025  }
1026
1027  v=owl_global_get_current_view(&g);
1028 
1029  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1030
1031  if (!m || owl_view_get_size(v)==0) {
1032    owl_function_error("No current message selected");
1033    return;
1034  }
1035
1036  text=owl_message_get_text(m);
1037
1038  /* First look for a good URL */ 
1039  if ((ptr1=strstr(text, "http://"))!=NULL) {
1040    ptr2=strpbrk(ptr1, " \n\t");
1041    if (ptr2) {
1042      strncpy(url, ptr1, ptr2-ptr1+1);
1043      url[ptr2-ptr1+1]='\0';
1044    } else {
1045      strcpy(url, ptr1);
1046    }
1047
1048    /* if we had <http strip a trailing > */
1049    if (ptr1>text && ptr1[-1]=='<') {
1050      if (url[strlen(url)-1]=='>') {
1051        url[strlen(url)-1]='\0';
1052      }
1053    }
1054  } else if ((ptr1=strstr(text, "https://"))!=NULL) {
1055    /* Look for an https URL */ 
1056    ptr2=strpbrk(ptr1, " \n\t");
1057    if (ptr2) {
1058      strncpy(url, ptr1, ptr2-ptr1+1);
1059      url[ptr2-ptr1+1]='\0';
1060    } else {
1061      strcpy(url, ptr1);
1062    }
1063   
1064    /* if we had <http strip a trailing > */
1065    if (ptr1>text && ptr1[-1]=='<') {
1066      if (url[strlen(url)-1]=='>') {
1067        url[strlen(url)-1]='\0';
1068      }
1069    }
1070  } else if ((ptr1=strstr(text, "www."))!=NULL) {
1071    /* if we can't find a real url look for www.something */
1072    ptr2=strpbrk(ptr1, " \n\t");
1073    if (ptr2) {
1074      strncpy(url, ptr1, ptr2-ptr1+1);
1075      url[ptr2-ptr1+1]='\0';
1076    } else {
1077      strcpy(url, ptr1);
1078    }
1079  } else {
1080    owl_function_beep();
1081    owl_function_error("Could not find URL to open.");
1082    return;
1083  }
1084
1085  /* Make sure there aren't any quotes or \'s in the url */
1086  for (ptr1 = url; *ptr1; ptr1++) {
1087    if (*ptr1 == '"' || *ptr1 == '\\') {
1088      owl_function_beep();
1089      owl_function_error("URL contains invalid characters.");
1090      return;
1091    }
1092  }
1093 
1094  /* NOTE: There are potentially serious security issues here... */
1095
1096  /* open the page */
1097  owl_function_makemsg("Opening %s", url);
1098  if (webbrowser == OWL_WEBBROWSER_NETSCAPE) {
1099    snprintf(tmpbuff, LINE, "netscape -remote \"openURL(%s)\" > /dev/null 2> /dev/null", url);
1100    system(tmpbuff); 
1101  } else if (webbrowser == OWL_WEBBROWSER_GALEON) {
1102    snprintf(tmpbuff, LINE, "galeon \"%s\" > /dev/null 2> /dev/null &", url);
1103    system(tmpbuff); 
1104  } else if (webbrowser == OWL_WEBBROWSER_OPERA) {
1105    snprintf(tmpbuff, LINE, "opera \"%s\" > /dev/null 2> /dev/null &", url);
1106    system(tmpbuff); 
1107  }
1108}
1109
1110void owl_function_calculate_topmsg(int direction)
1111{
1112  int recwinlines, topmsg, curmsg;
1113  owl_view *v;
1114
1115  v=owl_global_get_current_view(&g);
1116  curmsg=owl_global_get_curmsg(&g);
1117  topmsg=owl_global_get_topmsg(&g);
1118  recwinlines=owl_global_get_recwin_lines(&g);
1119
1120  /*
1121  if (owl_view_get_size(v) < 1) {
1122    return;
1123  }
1124  */
1125
1126  switch (owl_global_get_scrollmode(&g)) {
1127  case OWL_SCROLLMODE_TOP:
1128    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
1129    break;
1130  case OWL_SCROLLMODE_NEARTOP:
1131    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
1132    break;
1133  case OWL_SCROLLMODE_CENTER:
1134    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
1135    break;
1136  case OWL_SCROLLMODE_PAGED:
1137    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
1138    break;
1139  case OWL_SCROLLMODE_PAGEDCENTER:
1140    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
1141    break;
1142  case OWL_SCROLLMODE_NORMAL:
1143  default:
1144    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
1145  }
1146  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
1147  owl_global_set_topmsg(&g, topmsg);
1148}
1149
1150/* Returns what the new topmsg should be. 
1151 * Passed the last direction of movement,
1152 * the current view,
1153 * the current message number in the view,
1154 * the top message currently being displayed,
1155 * and the number of lines in the recwin.
1156 */
1157int owl_function_calculate_topmsg_top(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1158{
1159  return(curmsg);
1160}
1161
1162int owl_function_calculate_topmsg_neartop(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1163{
1164  if (curmsg>0 
1165      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1166          <  recwinlines/2)) {
1167    return(curmsg-1);
1168  } else {
1169    return(curmsg);
1170  }
1171}
1172 
1173int owl_function_calculate_topmsg_center(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1174{
1175  int i, last, lines;
1176
1177  last = curmsg;
1178  lines = 0;
1179  for (i=curmsg-1; i>=0; i--) {
1180    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1181    if (lines > recwinlines/2) break;
1182    last = i;
1183  }
1184  return(last);
1185}
1186 
1187int owl_function_calculate_topmsg_paged(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
1188{
1189  int i, last, lines, savey;
1190 
1191  /* If we're off the top of the screen, scroll up such that the
1192   * curmsg is near the botton of the screen. */
1193  if (curmsg < topmsg) {
1194    last = curmsg;
1195    lines = 0;
1196    for (i=curmsg; i>=0; i--) {
1197      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1198      if (lines > recwinlines) break;
1199    last = i;
1200    }
1201    if (center_on_page) {
1202      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1203    } else {
1204      return(last);
1205    }
1206  }
1207
1208  /* Find number of lines from top to bottom of curmsg (store in savey) */
1209  savey=0;
1210  for (i=topmsg; i<=curmsg; i++) {
1211    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1212  }
1213
1214  /* if we're off the bottom of the screen, scroll down */
1215  if (savey > recwinlines) {
1216    if (center_on_page) {
1217      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1218    } else {
1219      return(curmsg);
1220    }
1221  }
1222
1223  /* else just stay as we are... */
1224  return(topmsg);
1225}
1226
1227int owl_function_calculate_topmsg_normal(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1228{
1229  int savey, i, foo, y;
1230
1231  if (curmsg<0) return(topmsg);
1232   
1233  /* If we're off the top of the screen then center */
1234  if (curmsg<topmsg) {
1235    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1236  }
1237
1238  /* If curmsg is so far past topmsg that there are more messages than
1239     lines, skip the line counting that follows because we're
1240     certainly off screen.  */
1241  savey=curmsg-topmsg;
1242  if (savey <= recwinlines) {
1243    /* Find number of lines from top to bottom of curmsg (store in savey) */
1244    savey = 0;
1245    for (i=topmsg; i<=curmsg; i++) {
1246      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1247    }
1248  }
1249
1250  /* If we're off the bottom of the screen, set the topmsg to curmsg
1251   * and scroll upwards */
1252  if (savey > recwinlines) {
1253    topmsg=curmsg;
1254    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
1255    direction=OWL_DIRECTION_UPWARDS;
1256  }
1257 
1258  /* If our bottom line is less than 1/4 down the screen then scroll up */
1259  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1260    if (savey < (recwinlines / 4)) {
1261      y=0;
1262      for (i=curmsg; i>=0; i--) {
1263        foo=owl_message_get_numlines(owl_view_get_element(v, i));
1264        /* will we run the curmsg off the screen? */
1265        if ((foo+y) >= recwinlines) {
1266          i++;
1267          if (i>curmsg) i=curmsg;
1268          break;
1269        }
1270        /* have saved 1/2 the screen space? */
1271        y+=foo;
1272        if (y > (recwinlines / 2)) break;
1273      }
1274      if (i<0) i=0;
1275      return(i);
1276    }
1277  }
1278
1279  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1280    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1281    if (savey > ((recwinlines * 3)/4)) {
1282      y=0;
1283      /* count lines from the top until we can save 1/2 the screen size */
1284      for (i=topmsg; i<curmsg; i++) {
1285        y+=owl_message_get_numlines(owl_view_get_element(v, i));
1286        if (y > (recwinlines / 2)) break;
1287      }
1288      if (i==curmsg) {
1289        i--;
1290      }
1291      return(i+1);
1292    }
1293  }
1294
1295  return(topmsg);
1296}
1297
1298void owl_function_resize()
1299{
1300  owl_global_set_resize_pending(&g);
1301}
1302
1303void owl_function_run_buffercommand()
1304{
1305  owl_editwin_do_callback(owl_global_get_typwin(&g));
1306}
1307
1308void owl_function_debugmsg(char *fmt, ...)
1309{
1310  FILE *file;
1311  time_t now;
1312  char buff1[LINE], buff2[LINE];
1313  va_list ap;
1314  va_start(ap, fmt);
1315
1316  if (!owl_global_is_debug_fast(&g)) return;
1317
1318  file=fopen(owl_global_get_debug_file(&g), "a");
1319  if (!file) return;
1320
1321  now=time(NULL);
1322  strcpy(buff1, ctime(&now));
1323  buff1[strlen(buff1)-1]='\0';
1324
1325  owl_global_get_runtime_string(&g, buff2);
1326 
1327  fprintf(file, "[%i -  %s - %s]: ", (int) getpid(), buff1, buff2);
1328  vfprintf(file, fmt, ap);
1329  fprintf(file, "\n");
1330  fclose(file);
1331
1332  va_end(ap);
1333}
1334
1335void owl_function_refresh()
1336{
1337  owl_function_resize();
1338}
1339
1340void owl_function_beep()
1341{
1342  if (owl_global_is_bell(&g)) {
1343    beep();
1344    owl_global_set_needrefresh(&g); /* do we really need this? */
1345  }
1346}
1347
1348int owl_function_subscribe(char *class, char *inst, char *recip)
1349{
1350  int ret;
1351
1352  ret=owl_zephyr_sub(class, inst, recip);
1353  if (ret) {
1354    owl_function_error("Error subscribing.");
1355  } else {
1356    owl_function_makemsg("Subscribed.");
1357  }
1358  return(ret);
1359}
1360
1361void owl_function_unsubscribe(char *class, char *inst, char *recip)
1362{
1363  int ret;
1364
1365  ret=owl_zephyr_unsub(class, inst, recip);
1366  if (ret) {
1367    owl_function_error("Error subscribing.");
1368  } else {
1369    owl_function_makemsg("Unsubscribed.");
1370  }
1371}
1372
1373void owl_function_set_cursor(WINDOW *win)
1374{
1375  wnoutrefresh(win);
1376}
1377
1378void owl_function_full_redisplay()
1379{
1380  redrawwin(owl_global_get_curs_recwin(&g));
1381  redrawwin(owl_global_get_curs_sepwin(&g));
1382  /* Work around curses segfualts with windows off the screen */
1383  if (g.lines >= owl_global_get_typwin_lines(&g)+2)
1384      redrawwin(owl_global_get_curs_typwin(&g));
1385  if (g.lines >= 2)
1386      redrawwin(owl_global_get_curs_msgwin(&g));
1387
1388  wnoutrefresh(owl_global_get_curs_recwin(&g));
1389  wnoutrefresh(owl_global_get_curs_sepwin(&g));
1390  wnoutrefresh(owl_global_get_curs_typwin(&g));
1391  wnoutrefresh(owl_global_get_curs_msgwin(&g));
1392
1393  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
1394    owl_popwin_refresh(owl_global_get_popwin(&g));
1395  }
1396 
1397  sepbar("");
1398  owl_function_makemsg("");
1399
1400  owl_global_set_needrefresh(&g);
1401}
1402
1403void owl_function_popless_text(char *text)
1404{
1405  owl_popwin *pw;
1406  owl_viewwin *v;
1407
1408  pw=owl_global_get_popwin(&g);
1409  v=owl_global_get_viewwin(&g);
1410
1411  owl_popwin_up(pw);
1412  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
1413                        owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
1414                        text);
1415  owl_popwin_refresh(pw);
1416  owl_viewwin_redisplay(v, 0);
1417  owl_global_set_needrefresh(&g);
1418}
1419
1420void owl_function_popless_fmtext(owl_fmtext *fm)
1421{
1422  owl_popwin *pw;
1423  owl_viewwin *v;
1424
1425  pw=owl_global_get_popwin(&g);
1426  v=owl_global_get_viewwin(&g);
1427
1428  owl_popwin_up(pw);
1429  owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw),
1430                   owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
1431                   fm);
1432  owl_popwin_refresh(pw);
1433  owl_viewwin_redisplay(v, 0);
1434  owl_global_set_needrefresh(&g);
1435}
1436
1437void owl_function_popless_file(char *filename)
1438{
1439  owl_fmtext fm;
1440  FILE *file;
1441  char buff[1024];
1442
1443  file=fopen(filename, "r");
1444  if (!file) {
1445    owl_function_error("Could not open file: %s", filename);
1446    return;
1447  }
1448
1449  owl_fmtext_init_null(&fm);
1450  while (fgets(buff, 1024, file)) {
1451    owl_fmtext_append_normal(&fm, buff);
1452    /*    owl_fmtext_append_normal(&fm, "\n"); */
1453  }
1454
1455  owl_function_popless_fmtext(&fm);
1456  owl_fmtext_free(&fm);
1457  fclose(file);
1458}
1459
1460void owl_function_about()
1461{
1462  char buff[5000];
1463
1464  sprintf(buff, "This is barnowl version %s\n\n", OWL_VERSION_STRING);
1465  strcat(buff, "barnowl is a fork of the Owl zephyr client, written and\n");
1466  strcat(buff, "maintained by Alejandro Sedeno and Nelson Elhage at the\n");
1467  strcat(buff, "Massachusetts Institute of Technology. \n");
1468  strcat(buff, "\n");
1469  strcat(buff, "Owl was written by James Kretchmar. The first version, 0.5, was\n");
1470  strcat(buff, "released in March 2002.\n");
1471  strcat(buff, "\n");
1472  strcat(buff, "The name 'owl' was chosen in reference to the owls in the\n");
1473  strcat(buff, "Harry Potter novels, who are tasked with carrying messages\n");
1474  strcat(buff, "between Witches and Wizards. The name 'barnowl' was chosen\n");
1475  strcat(buff, "because we feel our owls should live closer to our ponies.\n");
1476  strcat(buff, "\n");
1477  strcat(buff, "Copyright (c) 2006-2008 The BarnOwl Developers. All rights reserved.\n");
1478  strcat(buff, "Copyright (c) 2004 James Kretchmar. All rights reserved.\n");
1479  strcat(buff, "Copyright 2002 Massachusetts Institute of Technology\n");
1480  strcat(buff, "\n");
1481  strcat(buff, "This program is free software. You can redistribute it and/or\n");
1482  strcat(buff,  "modify under the terms of the Sleepycat License. Use the \n");
1483  strcat(buff,  "':show license' command to display the full license\n");
1484  owl_function_popless_text(buff);
1485}
1486
1487void owl_function_info()
1488{
1489  owl_message *m;
1490  owl_fmtext fm, attrfm;
1491  char buff[10000];
1492  owl_view *v;
1493#ifdef HAVE_LIBZEPHYR
1494  ZNotice_t *n;
1495#endif
1496
1497  owl_fmtext_init_null(&fm);
1498 
1499  v=owl_global_get_current_view(&g);
1500  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1501  if (!m || owl_view_get_size(v)==0) {
1502    owl_function_error("No message selected\n");
1503    return;
1504  }
1505
1506  owl_fmtext_append_bold(&fm, "General Information:\n");
1507  owl_fmtext_append_normal(&fm, "  Msg Id    : ");
1508  sprintf(buff, "%i", owl_message_get_id(m));
1509  owl_fmtext_append_normal(&fm, buff);
1510  owl_fmtext_append_normal(&fm, "\n");
1511
1512  owl_fmtext_append_normal(&fm, "  Type      : ");
1513  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1514  owl_fmtext_append_normal(&fm, "\n");
1515
1516  if (owl_message_is_direction_in(m)) {
1517    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1518  } else if (owl_message_is_direction_out(m)) {
1519    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1520  } else if (owl_message_is_direction_none(m)) {
1521    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1522  } else {
1523    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1524  }
1525
1526  owl_fmtext_append_normal(&fm, "  Time      : ");
1527  owl_fmtext_append_normal(&fm, owl_message_get_timestr(m));
1528  owl_fmtext_append_normal(&fm, "\n");
1529
1530  if (!owl_message_is_type_admin(m)) {
1531    owl_fmtext_append_normal(&fm, "  Sender    : ");
1532    owl_fmtext_append_normal(&fm, owl_message_get_sender(m));
1533    owl_fmtext_append_normal(&fm, "\n");
1534   
1535    owl_fmtext_append_normal(&fm, "  Recipient : ");
1536    owl_fmtext_append_normal(&fm, owl_message_get_recipient(m));
1537    owl_fmtext_append_normal(&fm, "\n");
1538  }
1539   
1540  if (owl_message_is_type_zephyr(m)) {
1541    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1542   
1543    owl_fmtext_append_normal(&fm, "  Class     : ");
1544    owl_fmtext_append_normal(&fm, owl_message_get_class(m));
1545    owl_fmtext_append_normal(&fm, "\n");
1546    owl_fmtext_append_normal(&fm, "  Instance  : ");
1547    owl_fmtext_append_normal(&fm, owl_message_get_instance(m));
1548    owl_fmtext_append_normal(&fm, "\n");
1549    owl_fmtext_append_normal(&fm, "  Opcode    : ");
1550    owl_fmtext_append_normal(&fm, owl_message_get_opcode(m));
1551    owl_fmtext_append_normal(&fm, "\n");
1552   
1553    owl_fmtext_append_normal(&fm, "  Time      : ");
1554    owl_fmtext_append_normal(&fm, owl_message_get_timestr(m));
1555    owl_fmtext_append_normal(&fm, "\n");
1556#ifdef HAVE_LIBZEPHYR
1557    if (owl_message_is_direction_in(m)) {
1558      char *ptr, tmpbuff[1024];
1559      int i, j, fields, len;
1560
1561      n=owl_message_get_notice(m);
1562
1563      if (!owl_message_is_pseudo(m)) {
1564        owl_fmtext_append_normal(&fm, "  Kind      : ");
1565        if (n->z_kind==UNSAFE) {
1566          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1567        } else if (n->z_kind==UNACKED) {
1568          owl_fmtext_append_normal(&fm, "UNACKED\n");
1569        } else if (n->z_kind==ACKED) {
1570          owl_fmtext_append_normal(&fm, "ACKED\n");
1571        } else if (n->z_kind==HMACK) {
1572          owl_fmtext_append_normal(&fm, "HMACK\n");
1573        } else if (n->z_kind==HMCTL) {
1574          owl_fmtext_append_normal(&fm, "HMCTL\n");
1575        } else if (n->z_kind==SERVACK) {
1576          owl_fmtext_append_normal(&fm, "SERVACK\n");
1577        } else if (n->z_kind==SERVNAK) {
1578          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1579        } else if (n->z_kind==CLIENTACK) {
1580          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1581        } else if (n->z_kind==STAT) {
1582          owl_fmtext_append_normal(&fm, "STAT\n");
1583        } else {
1584          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1585        }
1586      }
1587      owl_fmtext_append_normal(&fm, "  Host      : ");
1588      owl_fmtext_append_normal(&fm, owl_message_get_hostname(m));
1589
1590      if (!owl_message_is_pseudo(m)) {
1591        owl_fmtext_append_normal(&fm, "\n");
1592        sprintf(buff, "  Port      : %i\n", ntohs(n->z_port));
1593        owl_fmtext_append_normal(&fm, buff);
1594
1595        owl_fmtext_append_normal(&fm,    "  Auth      : ");
1596        owl_fmtext_append_normal(&fm, owl_zephyr_get_authstr(n));
1597        owl_fmtext_append_normal(&fm, "\n");
1598       
1599        /* fix this */
1600        sprintf(buff, "  Checkd Ath: %i\n", n->z_checked_auth);
1601        sprintf(buff, "%s  Multi notc: %s\n", buff, n->z_multinotice);
1602        sprintf(buff, "%s  Num other : %i\n", buff, n->z_num_other_fields);
1603        sprintf(buff, "%s  Msg Len   : %i\n", buff, n->z_message_len);
1604        owl_fmtext_append_normal(&fm, buff);
1605       
1606        sprintf(buff, "  Fields    : %i\n", owl_zephyr_get_num_fields(n));
1607        owl_fmtext_append_normal(&fm, buff);
1608       
1609        fields=owl_zephyr_get_num_fields(n);
1610        for (i=0; i<fields; i++) {
1611          sprintf(buff, "  Field %i   : ", i+1);
1612         
1613          ptr=owl_zephyr_get_field_as_utf8(n, i+1);
1614          len=strlen(ptr);
1615          if (len<30) {
1616            strncpy(tmpbuff, ptr, len);
1617            tmpbuff[len]='\0';
1618          } else {
1619            strncpy(tmpbuff, ptr, 30);
1620            tmpbuff[30]='\0';
1621            strcat(tmpbuff, "...");
1622          }
1623          owl_free(ptr);
1624         
1625          for (j=0; j<strlen(tmpbuff); j++) {
1626            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1627            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1628          }
1629         
1630          strcat(buff, tmpbuff);
1631          strcat(buff, "\n");
1632          owl_fmtext_append_normal(&fm, buff);
1633        }
1634        owl_fmtext_append_normal(&fm, "  Default Fm:");
1635        owl_fmtext_append_normal(&fm, n->z_default_format);
1636      }
1637     
1638    }
1639#endif   
1640  }
1641
1642  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1643  owl_message_attributes_tofmtext(m, &attrfm);
1644  owl_fmtext_append_fmtext(&fm, &attrfm);
1645 
1646  owl_function_popless_fmtext(&fm);
1647  owl_fmtext_free(&fm);
1648  owl_fmtext_free(&attrfm);
1649}
1650
1651/* print the current message in a popup window.
1652 * Use the 'default' style regardless of whatever
1653 * style the user may be using
1654 */
1655void owl_function_curmsg_to_popwin()
1656{
1657  owl_popwin *pw;
1658  owl_view *v;
1659  owl_message *m;
1660  owl_style *s;
1661  owl_fmtext fm;
1662
1663  v=owl_global_get_current_view(&g);
1664  s=owl_global_get_style_by_name(&g, "default");
1665  pw=owl_global_get_popwin(&g);
1666
1667  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1668
1669  if (!m || owl_view_get_size(v)==0) {
1670    owl_function_error("No current message");
1671    return;
1672  }
1673
1674  owl_fmtext_init_null(&fm);
1675  owl_style_get_formattext(s, &fm, m);
1676
1677  owl_function_popless_fmtext(&fm);
1678  owl_fmtext_free(&fm);
1679}
1680
1681void owl_function_page_curmsg(int step)
1682{
1683  /* scroll down or up within the current message IF the message is truncated */
1684
1685  int offset, curmsg, lines;
1686  owl_view *v;
1687  owl_message *m;
1688
1689  offset=owl_global_get_curmsg_vert_offset(&g);
1690  v=owl_global_get_current_view(&g);
1691  curmsg=owl_global_get_curmsg(&g);
1692  m=owl_view_get_element(v, curmsg);
1693  if (!m || owl_view_get_size(v)==0) return;
1694  lines=owl_message_get_numlines(m);
1695
1696  if (offset==0) {
1697    /* Bail if the curmsg isn't the last one displayed */
1698    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1699      owl_function_makemsg("The entire message is already displayed");
1700      return;
1701    }
1702   
1703    /* Bail if we're not truncated */
1704    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1705      owl_function_makemsg("The entire message is already displayed");
1706      return;
1707    }
1708  }
1709 
1710 
1711  /* don't scroll past the last line */
1712  if (step>0) {
1713    if (offset+step > lines-1) {
1714      owl_global_set_curmsg_vert_offset(&g, lines-1);
1715    } else {
1716      owl_global_set_curmsg_vert_offset(&g, offset+step);
1717    }
1718  }
1719
1720  /* would we be before the beginning of the message? */
1721  if (step<0) {
1722    if (offset+step<0) {
1723      owl_global_set_curmsg_vert_offset(&g, 0);
1724    } else {
1725      owl_global_set_curmsg_vert_offset(&g, offset+step);
1726    }
1727  }
1728 
1729  /* redisplay */
1730  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1731  owl_global_set_needrefresh(&g);
1732}
1733
1734void owl_function_resize_typwin(int newsize)
1735{
1736  owl_global_set_typwin_lines(&g, newsize);
1737  owl_function_resize();
1738}
1739
1740void owl_function_typwin_grow()
1741{
1742  int i;
1743
1744  i=owl_global_get_typwin_lines(&g);
1745  owl_function_resize_typwin(i+1);
1746}
1747
1748void owl_function_typwin_shrink()
1749{
1750  int i;
1751
1752  i=owl_global_get_typwin_lines(&g);
1753  if (i>2) {
1754    owl_function_resize_typwin(i-1);
1755  }
1756}
1757
1758void owl_function_mainwin_pagedown()
1759{
1760  int i;
1761
1762  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1763  if (i<0) return;
1764  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1765      && (owl_global_get_curmsg(&g) < i)
1766      && (i>0)) {
1767    i--;
1768  }
1769  owl_global_set_curmsg(&g, i);
1770  owl_function_nextmsg();
1771}
1772
1773void owl_function_mainwin_pageup()
1774{
1775  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1776  owl_function_prevmsg();
1777}
1778
1779void owl_function_getsubs()
1780{
1781  char *buff;
1782
1783  buff=owl_zephyr_getsubs();
1784
1785  if (buff) {
1786    owl_function_popless_text(buff);
1787  } else {
1788    owl_function_popless_text("Error getting subscriptions");
1789  }
1790           
1791  owl_free(buff);
1792}
1793
1794#define PABUFLEN 5000
1795void owl_function_printallvars()
1796{
1797  char buff[PABUFLEN], *pos, *name;
1798  owl_list varnames;
1799  int i, numvarnames, rem;
1800
1801  pos = buff;
1802  pos += sprintf(pos, "%-20s = %s\n", "VARIABLE", "VALUE");
1803  pos += sprintf(pos, "%-20s   %s\n",  "--------", "-----");
1804  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1805  rem = (buff+PABUFLEN)-pos-1;
1806  numvarnames = owl_list_get_size(&varnames);
1807  for (i=0; i<numvarnames; i++) {
1808    name = owl_list_get_element(&varnames, i);
1809    if (name && name[0]!='_') {
1810      rem = (buff+PABUFLEN)-pos-1;   
1811      pos += snprintf(pos, rem, "\n%-20s = ", name);
1812      rem = (buff+PABUFLEN)-pos-1;   
1813      owl_variable_get_tostring(owl_global_get_vardict(&g), name, pos, rem);
1814      pos = buff+strlen(buff);
1815    }
1816  }
1817  rem = (buff+PABUFLEN)-pos-1;   
1818  snprintf(pos, rem, "\n");
1819  owl_variable_dict_namelist_free(&varnames);
1820 
1821  owl_function_popless_text(buff);
1822}
1823
1824void owl_function_show_variables()
1825{
1826  owl_list varnames;
1827  owl_fmtext fm; 
1828  int i, numvarnames;
1829  char *varname;
1830
1831  owl_fmtext_init_null(&fm);
1832  owl_fmtext_append_bold(&fm, 
1833      "Variables: (use 'show variable <name>' for details)\n");
1834  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1835  numvarnames = owl_list_get_size(&varnames);
1836  for (i=0; i<numvarnames; i++) {
1837    varname = owl_list_get_element(&varnames, i);
1838    if (varname && varname[0]!='_') {
1839      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1840    }
1841  }
1842  owl_variable_dict_namelist_free(&varnames);
1843  owl_function_popless_fmtext(&fm);
1844  owl_fmtext_free(&fm);
1845}
1846
1847void owl_function_show_variable(char *name)
1848{
1849  owl_fmtext fm; 
1850
1851  owl_fmtext_init_null(&fm);
1852  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1853  owl_function_popless_fmtext(&fm);
1854  owl_fmtext_free(&fm); 
1855}
1856
1857/* note: this applies to global message list, not to view.
1858 * If flag is 1, deletes.  If flag is 0, undeletes. */
1859void owl_function_delete_by_id(int id, int flag)
1860{
1861  owl_messagelist *ml;
1862  owl_message *m;
1863  ml = owl_global_get_msglist(&g);
1864  m = owl_messagelist_get_by_id(ml, id);
1865  if (m) {
1866    if (flag == 1) {
1867      owl_message_mark_delete(m);
1868    } else if (flag == 0) {
1869      owl_message_unmark_delete(m);
1870    }
1871    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1872    owl_global_set_needrefresh(&g);
1873  } else {
1874    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1875  }
1876}
1877
1878void owl_function_delete_automsgs()
1879{
1880  /* mark for deletion all messages in the current view that match the
1881   * 'trash' filter */
1882
1883  int i, j, count;
1884  owl_message *m;
1885  owl_view *v;
1886  owl_filter *f;
1887
1888  /* get the trash filter */
1889  f=owl_global_get_filter(&g, "trash");
1890  if (!f) {
1891    owl_function_error("No trash filter defined");
1892    return;
1893  }
1894
1895  v=owl_global_get_current_view(&g);
1896
1897  count=0;
1898  j=owl_view_get_size(v);
1899  for (i=0; i<j; i++) {
1900    m=owl_view_get_element(v, i);
1901    if (owl_filter_message_match(f, m)) {
1902      count++;
1903      owl_message_mark_delete(m);
1904    }
1905  }
1906  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1907  owl_function_makemsg("%i messages marked for deletion", count);
1908  owl_global_set_needrefresh(&g);
1909}
1910
1911void owl_function_status()
1912{
1913  char buff[5000];
1914  time_t start;
1915  int up, days, hours, minutes;
1916  owl_fmtext fm;
1917
1918  owl_fmtext_init_null(&fm);
1919
1920  start=owl_global_get_starttime(&g);
1921
1922  owl_fmtext_append_normal(&fm, "General Information:\n");
1923
1924  owl_fmtext_append_normal(&fm, "  Version: ");
1925  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1926  owl_fmtext_append_normal(&fm, "\n");
1927
1928  owl_fmtext_append_normal(&fm, "  Startup Arguments: ");
1929  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1930  owl_fmtext_append_normal(&fm, "\n");
1931
1932  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1933  (void) getcwd(buff, MAXPATHLEN);
1934  owl_fmtext_append_normal(&fm, buff);
1935  owl_fmtext_append_normal(&fm, "\n");
1936
1937  sprintf(buff, "  Startup Time: %s", ctime(&start));
1938  owl_fmtext_append_normal(&fm, buff);
1939
1940  up=owl_global_get_runtime(&g);
1941  days=up/86400;
1942  up-=days*86400;
1943  hours=up/3600;
1944  up-=hours*3600;
1945  minutes=up/60;
1946  up-=minutes*60;
1947  sprintf(buff, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1948  owl_fmtext_append_normal(&fm, buff);
1949
1950  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1951  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1952  if (owl_global_is_havezephyr(&g)) {
1953    owl_fmtext_append_normal(&fm, "yes\n");
1954  } else {
1955    owl_fmtext_append_normal(&fm, "no\n");
1956  }
1957  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1958  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1959
1960
1961  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1962  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1963#if OWL_STDERR_REDIR
1964  owl_fmtext_append_normal(&fm, "yes\n");
1965#else
1966  owl_fmtext_append_normal(&fm, "no\n");
1967#endif
1968 
1969
1970  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1971  owl_fmtext_append_normal(&fm, "  Logged in: ");
1972  if (owl_global_is_aimloggedin(&g)) {
1973    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1974    owl_fmtext_append_normal(&fm, "\n");
1975  } else {
1976    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1977  }
1978
1979  owl_fmtext_append_normal(&fm, "  Processing events: ");
1980  if (owl_global_is_doaimevents(&g)) {
1981    owl_fmtext_append_normal(&fm, "yes\n");
1982  } else {
1983    owl_fmtext_append_normal(&fm, "no\n");
1984  }
1985
1986  owl_function_popless_fmtext(&fm);
1987  owl_fmtext_free(&fm);
1988}
1989
1990void owl_function_show_term()
1991{
1992  owl_fmtext fm;
1993  char buff[LINE];
1994
1995  owl_fmtext_init_null(&fm);
1996  sprintf(buff, "Terminal Lines: %i\nTerminal Columns: %i\n",
1997          owl_global_get_lines(&g),
1998          owl_global_get_cols(&g));
1999  owl_fmtext_append_normal(&fm, buff);
2000
2001  if (owl_global_get_hascolors(&g)) {
2002    owl_fmtext_append_normal(&fm, "Color: Yes\n");
2003    sprintf(buff, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
2004    owl_fmtext_append_normal(&fm, buff);
2005    sprintf(buff, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
2006    owl_fmtext_append_normal(&fm, buff);
2007  } else {
2008    owl_fmtext_append_normal(&fm, "Color: No\n");
2009  }
2010
2011  owl_function_popless_fmtext(&fm);
2012  owl_fmtext_free(&fm);
2013}
2014
2015/* if type = 0 then normal reply.
2016 * if type = 1 then it's a reply to sender
2017 * if enter = 0 then allow the command to be edited
2018 * if enter = 1 then don't wait for editing
2019 */
2020void owl_function_reply(int type, int enter)
2021{
2022  char *buff=NULL, *oldbuff;
2023  owl_message *m;
2024  owl_filter *f;
2025 
2026  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
2027    owl_function_error("No message selected");
2028  } else {
2029    char *class, *inst, *to, *cc=NULL;
2030   
2031    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
2032    if (!m) {
2033      owl_function_error("No message selected");
2034      return;
2035    }
2036
2037    /* first check if we catch the reply-lockout filter */
2038    f=owl_global_get_filter(&g, "reply-lockout");
2039    if (f) {
2040      if (owl_filter_message_match(f, m)) {
2041        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
2042        return;
2043      }
2044    }
2045
2046    /* loopback */
2047    if (owl_message_is_type_loopback(m)) {
2048      owl_function_loopwrite_setup();
2049      return;
2050    }
2051
2052    /* zephyr */
2053    if (owl_message_is_type_zephyr(m)) {
2054      /* if it's a zephyr we sent, send it out the same way again */
2055      if (owl_message_is_direction_out(m)) {
2056          buff = owl_strdup(owl_message_get_zwriteline(m));
2057      } else {
2058
2059        /* Special case a personal reply to a webzephyr user on a class */
2060        if ((type==1) && !strcasecmp(owl_message_get_opcode(m), OWL_WEBZEPHYR_OPCODE)) {
2061          class=OWL_WEBZEPHYR_CLASS;
2062          inst=owl_message_get_sender(m);
2063          to=OWL_WEBZEPHYR_PRINCIPAL;
2064        } else if (!strcasecmp(owl_message_get_class(m), OWL_WEBZEPHYR_CLASS) && owl_message_is_loginout(m)) {
2065          /* Special case LOGIN/LOGOUT notifications on class "webzephyr" */
2066          class=OWL_WEBZEPHYR_CLASS;
2067          inst=owl_message_get_instance(m);
2068          to=OWL_WEBZEPHYR_PRINCIPAL;
2069        } else if (owl_message_is_loginout(m)) {
2070          /* Normal LOGIN/LOGOUT messages */
2071          class="MESSAGE";
2072          inst="PERSONAL";
2073          to=owl_message_get_sender(m);
2074        } else if (type==1) {
2075          /* Personal reply */
2076          class="MESSAGE";
2077          inst="PERSONAL";
2078          to=owl_message_get_sender(m);
2079        } else {
2080          /* General reply */
2081          class=owl_message_get_class(m);
2082          inst=owl_message_get_instance(m);
2083          to=owl_message_get_recipient(m);
2084          cc=owl_message_get_cc_without_recipient(m);
2085          if (!strcmp(to, "") || !strcmp(to, "*")) {
2086            to="";
2087          } else if (to[0]=='@') {
2088            /* leave it, to get the realm */
2089          } else {
2090            to=owl_message_get_sender(m);
2091          }
2092        }
2093
2094        /* create the command line */
2095        if (!strcasecmp(owl_message_get_opcode(m), "CRYPT")) {
2096          buff=owl_strdup("zcrypt");
2097        } else {
2098          buff = owl_strdup("zwrite");
2099        }
2100        if (strcasecmp(class, "message")) {
2101          buff = owl_sprintf("%s -c %s%s%s", oldbuff=buff, owl_getquoting(class), class, owl_getquoting(class));
2102          owl_free(oldbuff);
2103        }
2104        if (strcasecmp(inst, "personal")) {
2105          buff = owl_sprintf("%s -i %s%s%s", oldbuff=buff, owl_getquoting(inst), inst, owl_getquoting(inst));
2106          owl_free(oldbuff);
2107        }
2108        if (*to != '\0') {
2109          char *tmp, *oldtmp, *tmp2;
2110          tmp=short_zuser(to);
2111          if (cc) {
2112            tmp = owl_util_uniq(oldtmp=tmp, cc, "-");
2113            owl_free(oldtmp);
2114            buff = owl_sprintf("%s -C %s", oldbuff=buff, tmp);
2115            owl_free(oldbuff);
2116          } else {
2117            if (owl_global_is_smartstrip(&g)) {
2118              tmp2=tmp;
2119              tmp=owl_zephyr_smartstripped_user(tmp2);
2120              owl_free(tmp2);
2121            }
2122            buff = owl_sprintf("%s %s", oldbuff=buff, tmp);
2123            owl_free(oldbuff);
2124          }
2125          owl_free(tmp);
2126        }
2127        if (cc) owl_free(cc);
2128      }
2129    } else if (owl_message_is_type_aim(m)) {
2130      /* aim */
2131      if (owl_message_is_direction_out(m)) {
2132        buff=owl_sprintf("aimwrite %s", owl_message_get_recipient(m));
2133      } else {
2134        buff=owl_sprintf("aimwrite %s", owl_message_get_sender(m));
2135      }
2136    } else {
2137      char *cmd;
2138      if((type == 0 &&
2139          (cmd=owl_perlconfig_message_call_method(m, "replycmd", 0, NULL))) ||
2140         (type == 1 &&
2141          (cmd=owl_perlconfig_message_call_method(m, "replysendercmd", 0, NULL)))) {
2142        buff = cmd;
2143      }
2144    }
2145
2146    if(!buff) {
2147        owl_function_error("I don't know how to reply to that message.");
2148        return;
2149    }
2150   
2151    if (enter) {
2152      owl_history *hist = owl_global_get_cmd_history(&g);
2153      owl_history_store(hist, buff);
2154      owl_history_reset(hist);
2155      owl_function_command_norv(buff);
2156    } else {
2157      owl_function_start_command(buff);
2158    }
2159    owl_free(buff);
2160  }
2161}
2162
2163void owl_function_zlocate(int argc, char **argv, int auth)
2164{
2165  owl_fmtext fm;
2166  char *ptr, buff[LINE];
2167  int i;
2168
2169  owl_fmtext_init_null(&fm);
2170
2171  for (i=0; i<argc; i++) {
2172    ptr=long_zuser(argv[i]);
2173    owl_zephyr_zlocate(ptr, buff, auth);
2174    owl_fmtext_append_normal(&fm, buff);
2175    owl_free(ptr);
2176  }
2177
2178  owl_function_popless_fmtext(&fm);
2179  owl_fmtext_free(&fm);
2180}
2181
2182void owl_function_start_command(char *line)
2183{
2184  owl_editwin *tw;
2185
2186  tw=owl_global_get_typwin(&g);
2187  owl_global_set_typwin_active(&g);
2188  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, 
2189                        owl_global_get_cmd_history(&g));
2190
2191  owl_editwin_set_locktext(tw, "command: ");
2192  owl_global_set_needrefresh(&g);
2193
2194  owl_editwin_insert_string(tw, line);
2195  owl_editwin_redisplay(tw, 0);
2196
2197  owl_context_set_editline(owl_global_get_context(&g), tw);
2198  owl_function_activate_keymap("editline");
2199}
2200
2201void owl_function_start_question(char *line)
2202{
2203  owl_editwin *tw;
2204
2205  tw=owl_global_get_typwin(&g);
2206  owl_global_set_typwin_active(&g);
2207  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
2208
2209  owl_editwin_set_locktext(tw, line);
2210  owl_global_set_needrefresh(&g);
2211
2212  owl_editwin_redisplay(tw, 0);
2213
2214  owl_context_set_editresponse(owl_global_get_context(&g), tw);
2215  owl_function_activate_keymap("editresponse");
2216}
2217
2218void owl_function_start_password(char *line)
2219{
2220  owl_editwin *tw;
2221
2222  tw=owl_global_get_typwin(&g);
2223  owl_global_set_typwin_active(&g);
2224  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
2225  owl_editwin_set_echochar(tw, '*');
2226
2227  owl_editwin_set_locktext(tw, line);
2228  owl_global_set_needrefresh(&g);
2229
2230  owl_editwin_redisplay(tw, 0);
2231
2232  owl_context_set_editresponse(owl_global_get_context(&g), tw);
2233  owl_function_activate_keymap("editresponse");
2234}
2235
2236char *owl_function_exec(int argc, char **argv, char *buff, int type)
2237{
2238  /* if type == 1 display in a popup
2239   * if type == 2 display an admin messages
2240   * if type == 0 return output
2241   * else display in a popup
2242   */
2243  char *newbuff, *redirect = " 2>&1 < /dev/null";
2244  char *out, buff2[1024];
2245  int size;
2246  FILE *p;
2247
2248#if OWL_STDERR_REDIR
2249  redirect = " < /dev/null";
2250#endif
2251
2252  if (argc<2) {
2253    owl_function_error("Wrong number of arguments to the exec command");
2254    return NULL;
2255  }
2256
2257  buff = skiptokens(buff, 1);
2258  newbuff = owl_malloc(strlen(buff)+strlen(redirect)+1);
2259  strcpy(newbuff, buff);
2260  strcat(newbuff, redirect);
2261
2262  if (type == 1) {
2263    owl_popexec_new(newbuff);
2264  } else {
2265    p=popen(newbuff, "r");
2266    out=owl_malloc(1024);
2267    size=1024;
2268    strcpy(out, "");
2269    while (fgets(buff2, 1024, p)!=NULL) {
2270      size+=1024;
2271      out=owl_realloc(out, size);
2272      strcat(out, buff2);
2273    }
2274    pclose(p);
2275   
2276    if (type==1) {
2277      owl_function_popless_text(out);
2278    } else if (type==0) {
2279      return out;
2280    } else if (type==2) {
2281      owl_function_adminmsg(buff, out);
2282    } else {
2283      owl_function_popless_text(out);
2284    }
2285    owl_free(out);
2286  }
2287  return NULL;
2288}
2289
2290char *owl_function_perl(int argc, char **argv, char *buff, int type)
2291{
2292  /* if type == 1 display in a popup
2293   * if type == 2 display an admin messages
2294   * if type == 0 return output
2295   * else display in a popup
2296   */
2297  char *perlout;
2298
2299  if (argc<2) {
2300    owl_function_error("Wrong number of arguments to perl command");
2301    return NULL;
2302  }
2303
2304  /* consume first token (argv[0]) */
2305  buff = skiptokens(buff, 1);
2306
2307  perlout = owl_perlconfig_execute(buff);
2308  if (perlout) { 
2309    if (type==1) {
2310      owl_function_popless_text(perlout);
2311    } else if (type==2) {
2312      owl_function_adminmsg(buff, perlout);
2313    } else if (type==0) {
2314      return perlout;
2315    } else {
2316      owl_function_popless_text(perlout);
2317    }
2318    owl_free(perlout);
2319  }
2320  return NULL;
2321}
2322
2323/* Change the filter associated with the current view.
2324 * This also figures out which message in the new filter
2325 * should have the pointer.
2326 */
2327void owl_function_change_currentview_filter(char *filtname)
2328{
2329  owl_view *v;
2330  owl_filter *f;
2331  int curid=-1, newpos, curmsg;
2332  owl_message *curm=NULL;
2333
2334  v=owl_global_get_current_view(&g);
2335
2336  curmsg=owl_global_get_curmsg(&g);
2337  if (curmsg==-1) {
2338    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2339  } else {
2340    curm=owl_view_get_element(v, curmsg);
2341    if (curm) {
2342      curid=owl_message_get_id(curm);
2343      owl_view_save_curmsgid(v, curid);
2344    }
2345  }
2346
2347  f=owl_global_get_filter(&g, filtname);
2348  if (!f) {
2349    owl_function_error("Unknown filter %s", filtname);
2350    return;
2351  }
2352
2353  owl_view_new_filter(v, f);
2354
2355  /* Figure out what to set the current message to.
2356   * - If the view we're leaving has messages in it, go to the closest message
2357   *   to the last message pointed to in that view.
2358   * - If the view we're leaving is empty, try to restore the position
2359   *   from the last time we were in the new view.  */
2360  if (curm) {
2361    newpos = owl_view_get_nearest_to_msgid(v, curid);
2362  } else {
2363    newpos = owl_view_get_nearest_to_saved(v);
2364  }
2365
2366  owl_global_set_curmsg(&g, newpos);
2367  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2368  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2369  owl_global_set_direction_downwards(&g);
2370}
2371
2372/* Create a new filter, or replace an existing one
2373 * with a new definition.
2374 */
2375void owl_function_create_filter(int argc, char **argv)
2376{
2377  owl_filter *f;
2378  owl_view *v;
2379  int ret, inuse=0;
2380
2381  if (argc < 2) {
2382    owl_function_error("Wrong number of arguments to filter command");
2383    return;
2384  }
2385
2386  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2387
2388  v=owl_global_get_current_view(&g);
2389
2390  /* don't touch the all filter */
2391  if (!strcmp(argv[1], "all")) {
2392    owl_function_error("You may not change the 'all' filter.");
2393    return;
2394  }
2395
2396  /* deal with the case of trying change the filter color */
2397  if (argc==4 && !strcmp(argv[2], "-c")) {
2398    f=owl_global_get_filter(&g, argv[1]);
2399    if (!f) {
2400      owl_function_error("The filter '%s' does not exist.", argv[1]);
2401      return;
2402    }
2403    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2404      owl_function_error("The color '%s' is not available.", argv[3]);
2405      return;
2406    }
2407    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2408    owl_global_set_needrefresh(&g);
2409    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2410    return;
2411  }
2412  if (argc==4 && !strcmp(argv[2], "-b")) {
2413    f=owl_global_get_filter(&g, argv[1]);
2414    if (!f) {
2415      owl_function_error("The filter '%s' does not exist.", argv[1]);
2416      return;
2417    }
2418    if (owl_util_string_to_color(argv[3])==OWL_COLOR_INVALID) {
2419      owl_function_error("The color '%s' is not available.", argv[3]);
2420      return;
2421    }
2422    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2423    owl_global_set_needrefresh(&g);
2424    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2425    return;
2426  }
2427
2428  /* create the filter and check for errors */
2429  f=owl_malloc(sizeof(owl_filter));
2430  ret=owl_filter_init(f, argv[1], argc-2, argv+2);
2431  if (ret==-1) {
2432    owl_free(f);
2433    owl_function_error("Invalid filter");
2434    return;
2435  }
2436
2437  /* if the named filter is in use by the current view, remember it */
2438  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2439    inuse=1;
2440  }
2441
2442  /* if the named filter already exists, nuke it */
2443  if (owl_global_get_filter(&g, argv[1])) {
2444    owl_global_remove_filter(&g, argv[1]);
2445  }
2446
2447  /* add the filter */
2448  owl_global_add_filter(&g, f);
2449
2450  /* if it was in use by the current view then update */
2451  if (inuse) {
2452    owl_function_change_currentview_filter(argv[1]);
2453  }
2454  owl_global_set_needrefresh(&g);
2455  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2456}
2457
2458/* If 'filtername' does not start with 'not-' create a filter named
2459 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2460 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2461 * 'filtername' begins with 'not-' and a filter 'filtername' already
2462 * exists, then do nothing.  If the filter 'filtername' does not
2463 * exist, create it and define it as 'not filter <filtername>'
2464 *
2465 * Returns the name of the negated filter, which the caller must free.
2466 */
2467char *owl_function_create_negative_filter(char *filtername)
2468{
2469  char *newname;
2470  owl_filter *tmpfilt;
2471  char *argv[5];
2472
2473  owl_function_debugmsg("owl_function_create_negative_filter");
2474 
2475  if (!strncmp(filtername, "not-", 4)) {
2476    newname=owl_strdup(filtername+4);
2477  } else {
2478    newname=owl_sprintf("not-%s", filtername);
2479  }
2480
2481  tmpfilt=owl_global_get_filter(&g, newname);
2482  if (!tmpfilt) {
2483    argv[0]="filter"; /* anything is fine here */
2484    argv[1]=newname;
2485    argv[2]="not";
2486    argv[3]="filter";
2487    argv[4]=filtername;
2488    owl_function_create_filter(5, argv);
2489  }
2490
2491  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2492  return(newname);
2493}
2494
2495void owl_function_show_filters()
2496{
2497  owl_list *l;
2498  owl_filter *f;
2499  int i, j;
2500  owl_fmtext fm;
2501
2502  owl_fmtext_init_null(&fm);
2503
2504  l=owl_global_get_filterlist(&g);
2505  j=owl_list_get_size(l);
2506
2507  owl_fmtext_append_bold(&fm, "Filters:\n");
2508
2509  for (i=0; i<j; i++) {
2510    f=owl_list_get_element(l, i);
2511    owl_fmtext_append_normal(&fm, "   ");
2512    if (owl_global_get_hascolors(&g)) {
2513      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2514    } else {
2515      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2516    }
2517    owl_fmtext_append_normal(&fm, "\n");
2518  }
2519  owl_function_popless_fmtext(&fm);
2520  owl_fmtext_free(&fm);
2521}
2522
2523void owl_function_show_filter(char *name)
2524{
2525  owl_filter *f;
2526  char *buff;
2527
2528  f=owl_global_get_filter(&g, name);
2529  if (!f) {
2530    owl_function_error("There is no filter named %s", name);
2531    return;
2532  }
2533  buff = owl_filter_print(f);
2534  owl_function_popless_text(buff);
2535  owl_free(buff);
2536}
2537
2538void owl_function_show_zpunts()
2539{
2540  owl_filter *f;
2541  owl_list *fl;
2542  char buff[5000];
2543  char *tmp;
2544  owl_fmtext fm;
2545  int i, j;
2546
2547  owl_fmtext_init_null(&fm);
2548
2549  fl=owl_global_get_puntlist(&g);
2550  j=owl_list_get_size(fl);
2551  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2552
2553  for (i=0; i<j; i++) {
2554    f=owl_list_get_element(fl, i);
2555    snprintf(buff, sizeof(buff), "[% 2d] ", i+1);
2556    owl_fmtext_append_normal(&fm, buff);
2557    tmp = owl_filter_print(f);
2558    owl_fmtext_append_normal(&fm, tmp);
2559    owl_free(tmp);
2560    owl_fmtext_append_normal(&fm, buff);
2561  }
2562  owl_function_popless_fmtext(&fm);
2563  owl_fmtext_free(&fm);
2564}
2565
2566/* Create a filter for a class, instance if one doesn't exist.  If
2567 * instance is NULL then catch all messgaes in the class.  Returns the
2568 * name of the filter, which the caller must free.
2569 */
2570char *owl_function_classinstfilt(char *c, char *i) 
2571{
2572  owl_list *fl;
2573  owl_filter *f;
2574  char *argbuff, *filtname;
2575  char *tmpclass, *tmpinstance = NULL;
2576  char *class, *instance = NULL;
2577  int len;
2578
2579  class = owl_util_baseclass(c);
2580  if(i) {
2581    instance = owl_util_baseclass(i);
2582  }
2583
2584  fl=owl_global_get_filterlist(&g);
2585
2586  /* name for the filter */
2587  len=strlen(class)+30;
2588  if (instance) len+=strlen(instance);
2589  filtname=owl_malloc(len);
2590  if (!instance) {
2591    sprintf(filtname, "class-%s", class);
2592  } else {
2593    sprintf(filtname, "class-%s-instance-%s", class, instance);
2594  }
2595  /* downcase it */
2596  {
2597    char *temp = g_utf8_strdown(filtname, -1);
2598    if (temp) {
2599      owl_free(filtname);
2600      filtname = temp;
2601    }
2602  }
2603  /* turn spaces, single quotes, and double quotes into dots */
2604  owl_text_tr(filtname, ' ', '.');
2605  owl_text_tr(filtname, '\'', '.');
2606  owl_text_tr(filtname, '"', '.');
2607 
2608  /* if it already exists then go with it.  This lets users override */
2609  if (owl_global_get_filter(&g, filtname)) {
2610    return(filtname);
2611  }
2612
2613  /* create the new filter */
2614  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2615  owl_text_tr(tmpclass, ' ', '.');
2616  owl_text_tr(tmpclass, '\'', '.');
2617  owl_text_tr(tmpclass, '"', '.');
2618  if (instance) {
2619    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2620    owl_text_tr(tmpinstance, ' ', '.');
2621    owl_text_tr(tmpinstance, '\'', '.');
2622    owl_text_tr(tmpinstance, '"', '.');
2623  }
2624  len = strlen(tmpclass);
2625  if(tmpinstance) len += strlen(tmpinstance);
2626  len += 60;
2627  argbuff = owl_malloc(len);
2628  sprintf(argbuff, "class ^(un)*%s(\\.d)*$", tmpclass);
2629  if (tmpinstance) {
2630    sprintf(argbuff, "%s and ( instance ^(un)*%s(\\.d)*$ )", argbuff, tmpinstance);
2631  }
2632  owl_free(tmpclass);
2633  if (tmpinstance) owl_free(tmpinstance);
2634
2635  f=owl_malloc(sizeof(owl_filter));
2636  owl_filter_init_fromstring(f, filtname, argbuff);
2637
2638  /* add it to the global list */
2639  owl_global_add_filter(&g, f);
2640
2641  owl_free(argbuff);
2642  owl_free(class);
2643  if (instance) {
2644    owl_free(instance);
2645  }
2646  return(filtname);
2647}
2648
2649/* Create a filter for personal zephyrs to or from the specified
2650 * zephyr user.  Includes login/logout notifications for the user.
2651 * The name of the filter will be 'user-<user>'.  If a filter already
2652 * exists with this name, no new filter will be created.  This allows
2653 * the configuration to override this function.  Returns the name of
2654 * the filter, which the caller must free.
2655 */
2656char *owl_function_zuserfilt(char *user)
2657{
2658  owl_filter *f;
2659  char *argbuff, *longuser, *shortuser, *filtname;
2660
2661  /* stick the local realm on if it's not there */
2662  longuser=long_zuser(user);
2663  shortuser=short_zuser(user);
2664
2665  /* name for the filter */
2666  filtname=owl_malloc(strlen(shortuser)+20);
2667  sprintf(filtname, "user-%s", shortuser);
2668
2669  /* if it already exists then go with it.  This lets users override */
2670  if (owl_global_get_filter(&g, filtname)) {
2671    return(owl_strdup(filtname));
2672  }
2673
2674  /* create the new-internal filter */
2675  f=owl_malloc(sizeof(owl_filter));
2676
2677  argbuff=owl_malloc(strlen(longuser)+1000);
2678  sprintf(argbuff, "( type ^zephyr$ and filter personal and ");
2679  sprintf(argbuff, "%s ( ( direction ^in$ and sender ^%s$ ) or ( direction ^out$ and recipient ^%s$ ) ) )", argbuff, longuser, longuser);
2680  sprintf(argbuff, "%s or ( ( class ^login$ ) and ( sender ^%s$ ) )", argbuff, longuser);
2681
2682  owl_filter_init_fromstring(f, filtname, argbuff);
2683
2684  /* add it to the global list */
2685  owl_global_add_filter(&g, f);
2686
2687  /* free stuff */
2688  owl_free(argbuff);
2689  owl_free(longuser);
2690  owl_free(shortuser);
2691
2692  return(filtname);
2693}
2694
2695/* Create a filter for AIM IM messages to or from the specified
2696 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2697 * filter already exists with this name, no new filter will be
2698 * created.  This allows the configuration to override this function.
2699 * Returns the name of the filter, which the caller must free.
2700 */
2701char *owl_function_aimuserfilt(char *user)
2702{
2703  owl_filter *f;
2704  char *argbuff, *filtname;
2705  char *escuser;
2706
2707  /* name for the filter */
2708  filtname=owl_malloc(strlen(user)+40);
2709  sprintf(filtname, "aimuser-%s", user);
2710
2711  /* if it already exists then go with it.  This lets users override */
2712  if (owl_global_get_filter(&g, filtname)) {
2713    return(owl_strdup(filtname));
2714  }
2715
2716  /* create the new-internal filter */
2717  f=owl_malloc(sizeof(owl_filter));
2718
2719  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2720
2721  argbuff=owl_malloc(1000);
2722  sprintf(argbuff,
2723          "( type ^aim$ and ( ( sender ^%s$ and recipient ^%s$ ) or ( sender ^%s$ and recipient ^%s$ ) ) )",
2724          escuser, owl_global_get_aim_screenname_for_filters(&g),
2725          owl_global_get_aim_screenname_for_filters(&g), escuser);
2726
2727  owl_filter_init_fromstring(f, filtname, argbuff);
2728
2729  /* add it to the global list */
2730  owl_global_add_filter(&g, f);
2731
2732  /* free stuff */
2733  owl_free(argbuff);
2734  owl_free(escuser);
2735
2736  return(filtname);
2737}
2738
2739char *owl_function_typefilt(char *type)
2740{
2741  owl_filter *f;
2742  char *argbuff, *filtname;
2743
2744  /* name for the filter */
2745  filtname=owl_sprintf("type-%s", type);
2746
2747  /* if it already exists then go with it.  This lets users override */
2748  if (owl_global_get_filter(&g, filtname)) {
2749    return filtname;
2750  }
2751
2752  /* create the new-internal filter */
2753  f=owl_malloc(sizeof(owl_filter));
2754
2755  argbuff = owl_sprintf("type ^%s$", type);
2756
2757  owl_filter_init_fromstring(f, filtname, argbuff);
2758
2759  /* add it to the global list */
2760  owl_global_add_filter(&g, f);
2761
2762  /* free stuff */
2763  owl_free(argbuff);
2764
2765  return filtname;
2766}
2767
2768/* If flag is 1, marks for deletion.  If flag is 0,
2769 * unmarks for deletion. */
2770void owl_function_delete_curview_msgs(int flag)
2771{
2772  owl_view *v;
2773  int i, j;
2774
2775  v=owl_global_get_current_view(&g);
2776  j=owl_view_get_size(v);
2777  for (i=0; i<j; i++) {
2778    if (flag == 1) {
2779      owl_message_mark_delete(owl_view_get_element(v, i));
2780    } else if (flag == 0) {
2781      owl_message_unmark_delete(owl_view_get_element(v, i));
2782    }
2783  }
2784
2785  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2786
2787  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2788}
2789
2790/* Create a filter based on the current message.  Returns the name of
2791 * a filter or null.  The caller must free this name.
2792 *
2793 * if the curmsg is a personal zephyr return a filter name
2794 *    to the zephyr converstaion with that user.
2795 * If the curmsg is a zephyr class message, instance foo, recip *,
2796 *    return a filter name to the class, inst.
2797 * If the curmsg is a zephyr class message and type==0 then
2798 *    return a filter name for just the class.
2799 * If the curmsg is a zephyr class message and type==1 then
2800 *    return a filter name for the class and instance.
2801 * If the curmsg is a personal AIM message returna  filter
2802 *    name to the AIM conversation with that user
2803 */
2804char *owl_function_smartfilter(int type)
2805{
2806  owl_view *v;
2807  owl_message *m;
2808  char *zperson, *filtname=NULL;
2809  char *argv[1];
2810 
2811  v=owl_global_get_current_view(&g);
2812  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2813
2814  if (!m || owl_view_get_size(v)==0) {
2815    owl_function_error("No message selected\n");
2816    return(NULL);
2817  }
2818
2819  /* very simple handling of admin messages for now */
2820  if (owl_message_is_type_admin(m)) {
2821    return(owl_function_typefilt("admin"));
2822  }
2823
2824  /* very simple handling of loopback messages for now */
2825  if (owl_message_is_type_loopback(m)) {
2826    return(owl_function_typefilt("loopback"));
2827  }
2828
2829  /* aim messages */
2830  if (owl_message_is_type_aim(m)) {
2831    if (owl_message_is_direction_in(m)) {
2832      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2833    } else if (owl_message_is_direction_out(m)) {
2834      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2835    }
2836    return(filtname);
2837  }
2838
2839  /* narrow personal and login messages to the sender or recip as appropriate */
2840  if (owl_message_is_type_zephyr(m)) {
2841    if (owl_message_is_personal(m) || owl_message_is_loginout(m)) {
2842      if (owl_message_is_direction_in(m)) {
2843        zperson=short_zuser(owl_message_get_sender(m));
2844      } else {
2845        zperson=short_zuser(owl_message_get_recipient(m));
2846      }
2847      filtname=owl_function_zuserfilt(zperson);
2848      owl_free(zperson);
2849      return(filtname);
2850    }
2851
2852    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2853    if (!strcasecmp(owl_message_get_class(m), "message")) {
2854      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
2855      return(filtname);
2856    }
2857
2858    /* otherwise narrow to the class */
2859    if (type==0) {
2860      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL);
2861    } else if (type==1) {
2862      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
2863    }
2864    return(filtname);
2865  }
2866
2867  /* pass it off to perl */
2868  if(type) {
2869    argv[0] = "-i";
2870  };
2871  return owl_perlconfig_message_call_method(m, "smartfilter", type ? 1 : 0, argv);
2872}
2873
2874void owl_function_smartzpunt(int type)
2875{
2876  /* Starts a zpunt command based on the current class,instance pair.
2877   * If type=0, uses just class.  If type=1, uses instance as well. */
2878  owl_view *v;
2879  owl_message *m;
2880  char *cmd, *cmdprefix, *mclass, *minst;
2881 
2882  v=owl_global_get_current_view(&g);
2883  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2884
2885  if (!m || owl_view_get_size(v)==0) {
2886    owl_function_error("No message selected\n");
2887    return;
2888  }
2889
2890  /* for now we skip admin messages. */
2891  if (owl_message_is_type_admin(m)
2892      || owl_message_is_loginout(m)
2893      || !owl_message_is_type_zephyr(m)) {
2894    owl_function_error("smartzpunt doesn't support this message type.");
2895    return;
2896  }
2897
2898  mclass = owl_message_get_class(m);
2899  minst = owl_message_get_instance(m);
2900  if (!mclass || !*mclass || *mclass==' '
2901      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2902      || (type && (!minst || !*minst|| *minst==' '))) {
2903    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2904                         mclass, minst);
2905  } else {
2906    cmdprefix = "start-command zpunt ";
2907    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10);
2908    strcpy(cmd, cmdprefix);
2909    strcat(cmd, owl_getquoting(mclass));
2910    strcat(cmd, mclass);
2911    strcat(cmd, owl_getquoting(mclass));
2912    if (type) {
2913      strcat(cmd, " ");
2914      strcat(cmd, owl_getquoting(minst));
2915      strcat(cmd, minst);
2916      strcat(cmd, owl_getquoting(minst));
2917    } else {
2918      strcat(cmd, " *");
2919    }
2920    owl_function_command(cmd);
2921    owl_free(cmd);
2922  }
2923}
2924
2925/* Set the color of the current view's filter to
2926 * be 'color'
2927 */
2928void owl_function_color_current_filter(char *fgcolor, char *bgcolor)
2929{
2930  char *name;
2931
2932  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2933  owl_function_color_filter(name, fgcolor, bgcolor);
2934}
2935
2936/* Set the color of the filter 'filter' to be 'color'.  If the color
2937 * name does not exist, return -1, if the filter does not exist or is
2938 * the "all" filter, return -2.  Return 0 on success
2939 */
2940int owl_function_color_filter(char *filtname, char *fgcolor, char *bgcolor)
2941{
2942  owl_filter *f;
2943
2944  f=owl_global_get_filter(&g, filtname);
2945  if (!f) {
2946    owl_function_error("Unknown filter");
2947    return(-2);
2948  }
2949
2950  /* don't touch the all filter */
2951  if (!strcmp(filtname, "all")) {
2952    owl_function_error("You may not change the 'all' filter.");
2953    return(-2);
2954  }
2955
2956  if (owl_util_string_to_color(fgcolor)==OWL_COLOR_INVALID) {
2957    owl_function_error("No color named '%s' avilable.", fgcolor);
2958    return(-1);
2959  }
2960
2961
2962  if (bgcolor != NULL) {
2963    if (owl_util_string_to_color(bgcolor)==OWL_COLOR_INVALID) {
2964      owl_function_error("No color named '%s' avilable.", bgcolor);
2965      return(-1);
2966    }
2967    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2968  }
2969  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2970 
2971  owl_global_set_needrefresh(&g);
2972  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2973  return(0);
2974}
2975
2976void owl_function_show_colors()
2977{
2978  owl_fmtext fm;
2979  int i; 
2980 
2981  owl_fmtext_init_null(&fm);
2982  owl_fmtext_append_normal(&fm, "default: ");
2983  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2984
2985  owl_fmtext_append_normal(&fm,"red:      ");
2986  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2987
2988  owl_fmtext_append_normal(&fm,"green:    ");
2989  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2990
2991  owl_fmtext_append_normal(&fm,"yellow:   ");
2992  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2993
2994  owl_fmtext_append_normal(&fm,"blue:     ");
2995  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2996
2997  owl_fmtext_append_normal(&fm,"magenta:  ");
2998  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2999
3000  owl_fmtext_append_normal(&fm,"cyan:     ");
3001  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
3002
3003  owl_fmtext_append_normal(&fm,"white:    ");
3004  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
3005
3006  for(i = 8; i < COLORS; ++i) {
3007    char* str1 = owl_sprintf("%4i:     ",i);
3008    char* str2 = owl_sprintf("%i\n",i);
3009    owl_fmtext_append_normal(&fm,str1);
3010    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
3011    owl_free(str1);
3012     owl_free(str2);
3013  }
3014 
3015  owl_function_popless_fmtext(&fm);
3016  owl_fmtext_free(&fm);
3017}
3018
3019/* add the given class, inst, recip to the punt list for filtering.
3020 *   if direction==0 then punt
3021 *   if direction==1 then unpunt
3022 */
3023void owl_function_zpunt(char *class, char *inst, char *recip, int direction)
3024{
3025  char *buff;
3026  char *quoted;
3027
3028  buff=owl_malloc(strlen(class)+strlen(inst)+strlen(recip)+100);
3029  strcpy(buff, "class");
3030  if (!strcmp(class, "*")) {
3031    strcat(buff, " .*");
3032  } else {
3033    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
3034    owl_text_tr(quoted, ' ', '.');
3035    owl_text_tr(quoted, '\'', '.');
3036    owl_text_tr(quoted, '"', '.');
3037    sprintf(buff, "%s ^(un)*%s(\\.d)*$", buff, quoted);
3038    owl_free(quoted);
3039  }
3040  if (!strcmp(inst, "*")) {
3041    strcat(buff, " and instance .*");
3042  } else {
3043    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
3044    owl_text_tr(quoted, ' ', '.');
3045    owl_text_tr(quoted, '\'', '.');
3046    owl_text_tr(quoted, '"', '.');
3047    sprintf(buff, "%s and instance ^(un)*%s(\\.d)*$", buff, quoted);
3048    owl_free(quoted);
3049  }
3050  if (strcmp(recip, "*")) {
3051    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
3052    owl_text_tr(quoted, ' ', '.');
3053    owl_text_tr(quoted, '\'', '.');
3054    owl_text_tr(quoted, '"', '.');
3055    sprintf(buff, "%s and recipient ^%s$", buff, quoted);
3056    owl_free(quoted);
3057  }
3058
3059  owl_function_punt(buff, direction);
3060  owl_free(buff);
3061}
3062
3063void owl_function_punt(char *filter, int direction)
3064{
3065  owl_filter *f;
3066  owl_list *fl;
3067  int ret, i, j;
3068  fl=owl_global_get_puntlist(&g);
3069
3070  /* first, create the filter */
3071  f=malloc(sizeof(owl_filter));
3072
3073  owl_function_debugmsg("About to filter %s", filter);
3074  ret=owl_filter_init_fromstring(f, "punt-filter", filter);
3075  if (ret) {
3076    owl_function_error("Error creating filter for zpunt");
3077    owl_filter_free(f);
3078    return;
3079  }
3080
3081  /* Check for an identical filter */
3082  j=owl_list_get_size(fl);
3083  for (i=0; i<j; i++) {
3084    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
3085      owl_function_debugmsg("found an equivalent punt filter");
3086      /* if we're punting, then just silently bow out on this duplicate */
3087      if (direction==0) {
3088        owl_filter_free(f);
3089        return;
3090      }
3091
3092      /* if we're unpunting, then remove this filter from the puntlist */
3093      if (direction==1) {
3094        owl_filter_free(owl_list_get_element(fl, i));
3095        owl_list_remove_element(fl, i);
3096        owl_filter_free(f);
3097        return;
3098      }
3099    }
3100  }
3101
3102  owl_function_debugmsg("punting");
3103  /* If we're punting, add the filter to the global punt list */
3104  if (direction==0) {
3105    owl_list_append_element(fl, f);
3106  }
3107}
3108
3109void owl_function_activate_keymap(char *keymap)
3110{
3111  if (!owl_keyhandler_activate(owl_global_get_keyhandler(&g), keymap)) {
3112    owl_function_error("Unable to activate keymap '%s'", keymap);
3113  }
3114}
3115
3116void owl_function_show_keymaps()
3117{
3118  owl_list l;
3119  owl_fmtext fm;
3120  owl_keymap *km;
3121  owl_keyhandler *kh;
3122  int i, numkm;
3123  char *kmname;
3124
3125  kh = owl_global_get_keyhandler(&g);
3126  owl_fmtext_init_null(&fm);
3127  owl_fmtext_append_bold(&fm, "Keymaps:   ");
3128  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
3129  owl_keyhandler_get_keymap_names(kh, &l);
3130  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
3131  owl_fmtext_append_normal(&fm, "\n");
3132
3133  numkm = owl_list_get_size(&l);
3134  for (i=0; i<numkm; i++) {
3135    kmname = owl_list_get_element(&l, i);
3136    km = owl_keyhandler_get_keymap(kh, kmname);
3137    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
3138    owl_keymap_get_details(km, &fm);   
3139  }
3140  owl_fmtext_append_normal(&fm, "\n");
3141 
3142  owl_function_popless_fmtext(&fm);
3143  owl_keyhandler_keymap_namelist_free(&l);
3144  owl_fmtext_free(&fm);
3145}
3146
3147char *owl_function_keymap_summary(void *name)
3148{
3149  owl_keymap *km
3150    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
3151  if (km) return owl_keymap_summary(km);
3152  else return(NULL);
3153}
3154
3155/* TODO: implement for real */
3156void owl_function_show_keymap(char *name)
3157{
3158  owl_fmtext fm;
3159  owl_keymap *km;
3160
3161  owl_fmtext_init_null(&fm);
3162  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
3163  if (km) {
3164    owl_keymap_get_details(km, &fm);
3165  } else {
3166    owl_fmtext_append_normal(&fm, "No such keymap...\n");
3167  } 
3168  owl_function_popless_fmtext(&fm);
3169  owl_fmtext_free(&fm);
3170}
3171
3172void owl_function_help_for_command(char *cmdname)
3173{
3174  owl_fmtext fm;
3175
3176  owl_fmtext_init_null(&fm);
3177  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
3178  owl_function_popless_fmtext(&fm); 
3179  owl_fmtext_free(&fm);
3180}
3181
3182void owl_function_search_start(char *string, int direction)
3183{
3184  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
3185  owl_global_set_search_active(&g, string);
3186  owl_function_search_helper(0, direction);
3187}
3188
3189void owl_function_search_continue(int direction)
3190{
3191  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
3192  owl_function_search_helper(1, direction);
3193}
3194
3195void owl_function_search_helper(int mode, int direction)
3196{
3197  /* move to a message that contains the string.  If direction is
3198   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
3199   * OWL_DIRECTION_UPWARDS then search backwards.
3200   *
3201   * If mode==0 then it will stay on the current message if it
3202   * contains the string.
3203   */
3204
3205  owl_view *v;
3206  int viewsize, i, curmsg, start;
3207  owl_message *m;
3208
3209  v=owl_global_get_current_view(&g);
3210  viewsize=owl_view_get_size(v);
3211  curmsg=owl_global_get_curmsg(&g);
3212 
3213  if (viewsize==0) {
3214    owl_function_error("No messages present");
3215    return;
3216  }
3217
3218  if (mode==0) {
3219    start=curmsg;
3220  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
3221    start=curmsg+1;
3222  } else {
3223    start=curmsg-1;
3224  }
3225
3226  /* bounds check */
3227  if (start>=viewsize || start<0) {
3228    owl_function_error("No further matches found");
3229    return;
3230  }
3231
3232  for (i=start; i<viewsize && i>=0;) {
3233    m=owl_view_get_element(v, i);
3234    if (owl_message_search(m, owl_global_get_search_string(&g))) {
3235      owl_global_set_curmsg(&g, i);
3236      owl_function_calculate_topmsg(direction);
3237      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3238      if (direction==OWL_DIRECTION_DOWNWARDS) {
3239        owl_global_set_direction_downwards(&g);
3240      } else {
3241        owl_global_set_direction_upwards(&g);
3242      }
3243      return;
3244    }
3245    if (direction==OWL_DIRECTION_DOWNWARDS) {
3246      i++;
3247    } else {
3248      i--;
3249    }
3250  }
3251  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3252  owl_function_error("No matches found");
3253}
3254
3255/* strips formatting from ztext and returns the unformatted text.
3256 * caller is responsible for freeing. */
3257char *owl_function_ztext_stylestrip(char *zt)
3258{
3259  owl_fmtext fm;
3260  char *plaintext;
3261
3262  owl_fmtext_init_null(&fm);
3263  owl_fmtext_append_ztext(&fm, zt);
3264  plaintext = owl_fmtext_print_plain(&fm);
3265  owl_fmtext_free(&fm);
3266  return(plaintext);
3267}
3268
3269/* Popup a buddylisting.  If filename is NULL use the default .anyone */
3270void owl_function_buddylist(int aim, int zephyr, char *filename)
3271{
3272  int i, j, x, idle;
3273  owl_fmtext fm;
3274  owl_buddylist *bl;
3275  owl_buddy *b;
3276  owl_list anyone;
3277  char *foo, *timestr;
3278#ifdef HAVE_LIBZEPHYR
3279  char *tmp, *user, *line;
3280  ZLocations_t location[200];
3281  int numlocs, ret;
3282#endif
3283
3284  owl_fmtext_init_null(&fm);
3285
3286  /* AIM first */
3287  if (aim && owl_global_is_aimloggedin(&g)) {
3288    bl=owl_global_get_buddylist(&g);
3289
3290    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3291    /* we're assuming AIM for now */
3292    j=owl_buddylist_get_size(bl);
3293    for (i=0; i<j; i++) {
3294      b=owl_buddylist_get_buddy_n(bl, i);
3295      idle=owl_buddy_get_idle_time(b);
3296      if (idle!=0) {
3297        timestr=owl_util_minutes_to_timestr(idle);
3298      } else {
3299        timestr=owl_strdup("");
3300      }
3301      foo=owl_sprintf("  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3302      owl_fmtext_append_normal(&fm, foo);
3303      owl_free(timestr);
3304      owl_free(foo);
3305    }
3306  }
3307
3308#ifdef HAVE_LIBZEPHYR
3309  if (zephyr) {
3310    if(!owl_global_is_havezephyr(&g)) {
3311      owl_function_error("Zephyr currently not available.");
3312    } else {
3313      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3314      owl_list_create(&anyone);
3315      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3316      if (ret) {
3317        owl_fmtext_append_normal(&fm, "  Error opening file for zephyr buddies.\n");
3318      } else {
3319        j=owl_list_get_size(&anyone);
3320        for (i=0; i<j; i++) {
3321          user=owl_list_get_element(&anyone, i);
3322          ret=ZLocateUser(user, &numlocs, ZAUTH);
3323          if (ret!=ZERR_NONE) {
3324            owl_function_error("Error getting location for %s", user);
3325            continue;
3326          }
3327
3328          numlocs=200;
3329          ret=ZGetLocations(location, &numlocs);
3330          if (ret==0) {
3331            for (x=0; x<numlocs; x++) {
3332              line=owl_malloc(strlen(location[x].host)+strlen(location[x].time)+strlen(location[x].tty)+100);
3333              tmp=short_zuser(user);
3334              sprintf(line, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3335                      tmp,
3336                      location[x].host,
3337                      location[x].tty,
3338                      location[x].time);
3339              owl_fmtext_append_normal(&fm, line);
3340              owl_free(tmp);
3341              owl_free(line);
3342            }
3343            if (numlocs>=200) {
3344              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3345            }
3346          }
3347        }
3348      }
3349      owl_list_free_all(&anyone, owl_free);
3350    } 
3351  }
3352#endif
3353
3354  if(aim && zephyr) {
3355      if(owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3356          char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3357          if(perlblist) {
3358              owl_fmtext_append_ztext(&fm, perlblist);
3359              owl_free(perlblist);
3360          }
3361      }
3362  }
3363 
3364  owl_function_popless_fmtext(&fm);
3365  owl_fmtext_free(&fm);
3366}
3367
3368/* Dump messages in the current view to the file 'filename'. */
3369void owl_function_dump(char *filename) 
3370{
3371  int i, j, count;
3372  owl_message *m;
3373  owl_view *v;
3374  FILE *file;
3375  char *plaintext;
3376
3377  v=owl_global_get_current_view(&g);
3378
3379  /* in the future make it ask yes/no */
3380  /*
3381  ret=stat(filename, &sbuf);
3382  if (!ret) {
3383    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3384    if (!ret) return;
3385  }
3386  */
3387
3388  file=fopen(filename, "w");
3389  if (!file) {
3390    owl_function_error("Error opening file");
3391    return;
3392  }
3393
3394  count=0;
3395  j=owl_view_get_size(v);
3396  for (i=0; i<j; i++) {
3397    m=owl_view_get_element(v, i);
3398    plaintext = owl_strip_format_chars(owl_message_get_text(m));
3399    if (plaintext) {
3400      fputs(plaintext, file);
3401      owl_free(plaintext);
3402    }
3403  }
3404  fclose(file);
3405  owl_function_makemsg("Messages dumped to %s", filename);
3406}
3407
3408void owl_function_do_newmsgproc(void)
3409{
3410  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3411    /* if there's a process out there, we need to check on it */
3412    if (owl_global_get_newmsgproc_pid(&g)) {
3413      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3414      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3415      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3416      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3417        /* it exited */
3418        owl_global_set_newmsgproc_pid(&g, 0);
3419        owl_function_debugmsg("newmsgproc exited");
3420      } else {
3421        owl_function_debugmsg("newmsgproc did not exit");
3422      }
3423    }
3424   
3425    /* if it exited, fork & exec a new one */
3426    if (owl_global_get_newmsgproc_pid(&g)==0) {
3427      int i, myargc;
3428      i=fork();
3429      if (i) {
3430        /* parent set the child's pid */
3431        owl_global_set_newmsgproc_pid(&g, i);
3432        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3433      } else {
3434        /* child exec's the program */
3435        char **parsed;
3436        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3437        if (myargc < 0) {
3438          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3439        }
3440        if (myargc <= 0) {
3441          _exit(127);
3442        }
3443        parsed=owl_realloc(parsed, sizeof(*parsed) * (myargc+1));
3444        parsed[myargc] = NULL;
3445       
3446        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3447       
3448        execvp(parsed[0], parsed);
3449       
3450       
3451        /* was there an error exec'ing? */
3452        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3453                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3454        _exit(127);
3455      }
3456    }
3457  }
3458}
3459
3460/* print the xterm escape sequence to raise the window */
3461void owl_function_xterm_raise(void)
3462{
3463  printf("\033[5t");
3464}
3465
3466/* print the xterm escape sequence to deiconify the window */
3467void owl_function_xterm_deiconify(void)
3468{
3469  printf("\033[1t");
3470}
3471
3472/* Add the specified command to the startup file.  Eventually this
3473 * should be clever, and rewriting settings that will obviosly
3474 * override earlier settings with 'set' 'bindkey' and 'alias'
3475 * commands.  For now though we just remove any line that would
3476 * duplicate this one and then append this line to the end of
3477 * startupfile.
3478 */
3479void owl_function_addstartup(char *buff)
3480{
3481  FILE *file;
3482  char *filename;
3483
3484  filename=owl_global_get_startupfile(&g);
3485  file=fopen(filename, "a");
3486  if (!file) {
3487    owl_function_error("Error opening startupfile for new command");
3488    return;
3489  }
3490
3491  /* delete earlier copies */
3492  owl_util_file_deleteline(filename, buff, 1);
3493
3494  /* add this line */
3495  fprintf(file, "%s\n", buff);
3496
3497  fclose(file);
3498}
3499
3500/* Remove the specified command from the startup file. */
3501void owl_function_delstartup(char *buff)
3502{
3503  char *filename;
3504  filename=owl_global_get_startupfile(&g);
3505  owl_util_file_deleteline(filename, buff, 1);
3506}
3507
3508/* Execute owl commands from the given filename.  If the filename
3509 * is NULL, use the default owl startup commands file.
3510 */
3511void owl_function_source(char *filename)
3512{
3513  FILE *file;
3514  char buff[LINE];
3515  int fail_silent = 0;
3516
3517  if (!filename) {
3518    fail_silent = 1;
3519    filename=owl_global_get_startupfile(&g);
3520  }
3521  file=fopen(filename, "r");
3522  if (!file) {
3523    if (!fail_silent) {
3524      owl_function_error("Error opening file: %s", filename);
3525    }
3526    return;
3527  }
3528  while (fgets(buff, LINE, file)!=NULL) {
3529    if (buff[0] == '#') continue;
3530    buff[strlen(buff)-1]='\0';
3531    owl_function_command(buff);
3532  }
3533  fclose(file);
3534}
3535
3536void owl_function_change_style(owl_view *v, char *stylename)
3537{
3538  owl_style *s;
3539
3540  s=owl_global_get_style_by_name(&g, stylename);
3541  if (!s) {
3542    owl_function_error("No style named %s", stylename);
3543    return;
3544  }
3545  owl_view_set_style(v, s);
3546  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3547  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3548  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3549}
3550
3551void owl_function_toggleoneline()
3552{
3553  owl_view *v;
3554  owl_style *s;
3555
3556  v=owl_global_get_current_view(&g);
3557  s=owl_view_get_style(v);
3558
3559  if (!owl_style_matches_name(s, "oneline")) {
3560    owl_function_change_style(v, "oneline");
3561  } else {
3562    owl_function_change_style(v, owl_global_get_default_style(&g));
3563  }
3564
3565  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3566  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3567  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3568}
3569
3570void owl_function_error(char *fmt, ...)
3571{
3572  va_list ap;
3573  char buff[2048], buff2[2048];
3574  char *date;
3575  time_t now;
3576
3577  now=time(NULL);
3578  date=owl_strdup(ctime(&now));
3579  date[strlen(date)-1]='\0';
3580
3581  va_start(ap, fmt);
3582
3583  vsnprintf(buff, 2048, fmt, ap);
3584  sprintf(buff2, "%s %s", date, buff);
3585  owl_function_debugmsg("ERROR: %s", buff);
3586  if (owl_global_get_curs_msgwin(&g)) {
3587    werase(owl_global_get_curs_msgwin(&g));
3588    waddstr(owl_global_get_curs_msgwin(&g), buff); 
3589    wnoutrefresh(owl_global_get_curs_msgwin(&g));
3590    owl_global_set_needrefresh(&g);
3591  }
3592  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff2);
3593  va_end(ap);
3594  owl_free(date);
3595}
3596
3597void owl_function_showerrs()
3598{
3599  owl_fmtext fm;
3600
3601  owl_fmtext_init_null(&fm);
3602  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3603  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3604  owl_function_popless_fmtext(&fm);
3605}
3606
3607void owl_function_makemsg(char *fmt, ...)
3608{
3609  va_list ap;
3610  char buff[2048];
3611
3612  if (!owl_global_get_curs_msgwin(&g)) return;
3613
3614  va_start(ap, fmt);
3615  werase(owl_global_get_curs_msgwin(&g));
3616 
3617  vsnprintf(buff, 2048, fmt, ap);
3618  owl_function_debugmsg("makemsg: %s", buff);
3619  waddstr(owl_global_get_curs_msgwin(&g), buff); 
3620  wnoutrefresh(owl_global_get_curs_msgwin(&g));
3621  owl_global_set_needrefresh(&g);
3622  va_end(ap);
3623}
3624
3625/* get locations for everyone in .anyone.  If 'notify' is '1' then
3626 * send a pseudo login or logout message for everyone not in sync with
3627 * the global zephyr buddy list.  The list is updated regardless of
3628 * the status of 'notify'.
3629 */
3630void owl_function_zephyr_buddy_check(int notify)
3631{
3632#ifdef HAVE_LIBZEPHYR
3633  int i, j;
3634  owl_list anyone;
3635  owl_message *m;
3636  owl_zbuddylist *zbl;
3637  char *user;
3638  ZLocations_t location[200];
3639  int numlocs, ret;
3640
3641  zbl=owl_global_get_zephyr_buddylist(&g);
3642
3643  owl_list_create(&anyone);
3644  ret=owl_zephyr_get_anyone_list(&anyone, NULL);
3645
3646  j=owl_list_get_size(&anyone);
3647  for (i=0; i<j; i++) {
3648    user=owl_list_get_element(&anyone, i);
3649    ret=ZLocateUser(user, &numlocs, ZAUTH);
3650    if (ret!=ZERR_NONE) {
3651      owl_function_error("Error getting location for %s", user);
3652      continue;
3653    }
3654    numlocs=200;
3655    ret=ZGetLocations(location, &numlocs);
3656    if (ret==0) {
3657      if ((numlocs>0) && !owl_zbuddylist_contains_user(zbl, user)) {
3658        /* Send a PSEUDO LOGIN! */
3659        if (notify) {
3660          m=owl_malloc(sizeof(owl_message));
3661          owl_message_create_pseudo_zlogin(m, 0, user, location[0].host, location[0].time, location[0].tty);
3662          owl_global_messagequeue_addmsg(&g, m);
3663        }
3664        owl_zbuddylist_adduser(zbl, user);
3665        owl_function_debugmsg("owl_function_zephyr_buddy_check: login for %s ", user);
3666      } else if ((numlocs==0) && owl_zbuddylist_contains_user(zbl, user)) {
3667        /* I don't think this ever happens (if there are 0 locations we should get an error from
3668         * ZGetLocations)
3669         */
3670        owl_function_error("owl_function_zephyr_buddy_check: exceptional case logout for %s ",user);
3671      }
3672    } else if ((ret==ZERR_NOLOCATIONS) && owl_zbuddylist_contains_user(zbl, user)) {
3673      /* Send a PSEUDO LOGOUT! */
3674      if (notify) {
3675        m=owl_malloc(sizeof(owl_message));
3676        owl_message_create_pseudo_zlogin(m, 1, user, "", "", "");
3677        owl_global_messagequeue_addmsg(&g, m);
3678      }
3679      owl_zbuddylist_deluser(zbl, user);
3680      owl_function_debugmsg("owl_function_zephyr_buddy_check: logout for %s ",user);
3681    }
3682  }
3683
3684  owl_list_free_all(&anyone, owl_free);
3685#endif
3686}
3687
3688void owl_function_aimsearch_results(char *email, owl_list *namelist)
3689{
3690  owl_fmtext fm;
3691  int i, j;
3692
3693  owl_fmtext_init_null(&fm);
3694  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3695  owl_fmtext_append_normal(&fm, email);
3696  owl_fmtext_append_normal(&fm, ":\n");
3697
3698  j=owl_list_get_size(namelist);
3699  for (i=0; i<j; i++) {
3700    owl_fmtext_append_normal(&fm, "  ");
3701    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3702    owl_fmtext_append_normal(&fm, "\n");
3703  }
3704
3705  owl_function_popless_fmtext(&fm);
3706  owl_fmtext_free(&fm);
3707}
3708
3709int owl_function_get_color_count()
3710{
3711     return COLORS;
3712}
Note: See TracBrowser for help on using the repository browser.