]> cgit.babelmonkeys.de Git - jubjub.git/blobdiff - src/gui/cli/JubCLIUI.m
Move away from BOOL
[jubjub.git] / src / gui / cli / JubCLIUI.m
index 6f0b03a68c5d51440a6c13d36b8f5850f7845a27..bcae48044ff354aec8d4dc68593bf161ea652f0c 100644 (file)
@@ -4,6 +4,8 @@
 #import "JubCLICommand.h"
 #import "JubCLIUI.h"
 
+#include <string.h>
+
 BEGINCLICOMMAND(JubCLIReplyCommand, @":r", nil,
     @"Sets the sender of the last incomming message as the default recipient")
 {
@@ -132,10 +134,45 @@ BEGINCLICOMMAND(JubCLIRosterCommand, @":roster", nil, @"Shows your roster")
 }
 ENDCLICOMMAND
 
+BEGINCLICOMMAND(JubCLISubsCommand, @":subs",
+    @"[list | ack <who> | nak <who>]",
+    @"Lists, acknowledges or denies subscription requests")
+{
+       if ([parameters count] < 1) {
+               [of_stdout writeLine: @"Syntax: ':subs "
+                   @"[list | ack <who> | nak <who>]'"];
+               return;
+       }
+
+       OFString *action = parameters[0];
+       if (![action isEqual: @"list"] && ([parameters count] != 2)) {
+               [of_stdout writeLine: @"Syntax: ':subs "
+                   @"[list | ack <who> | nak <who>]'"];
+               return;
+       }
+
+       if ([action isEqual: @"list"]) {
+               for (OFString *request in _ui.subRequests)
+                       [of_stdout writeLine: request];
+               return;
+       }
+
+       XMPPJID *JID = [XMPPJID JIDWithString: parameters[1]];
+
+       if ([action isEqual: @"ack"])
+               [_ui.client.contactManager sendSubscribedToJID: JID];
+       else if ([action isEqual: @"nak"])
+               [_ui.client.contactManager sendUnsubscribedToJID: JID];
+
+       [_ui.subRequests removeObject: parameters[1]];
+}
+ENDCLICOMMAND
+
 @implementation JubCLIUI
 @synthesize client = _client;
 @synthesize lastIn = _lastIn;
 @synthesize sink = _sink;
+@synthesize subRequests = _subRequests;
 
 - initWithClient: (JubChatClient*)client
 {
@@ -143,6 +180,7 @@ ENDCLICOMMAND
 
        @try {
                _commands =  [OFMutableDictionary new];
+               _subRequests =  [OFMutableSet new];
                _client = [client retain];
                _contactManager = client.contactManager;
                [_contactManager addDelegate: self];
@@ -161,6 +199,8 @@ ENDCLICOMMAND
 
                [self addCommand: [[[JubCLIRosterCommand alloc]
                                       initWithCLIUI: self] autorelease]];
+               [self addCommand: [[[JubCLISubsCommand alloc]
+                                      initWithCLIUI: self] autorelease]];
        } @catch (id e) {
                [self release];
                @throw e;
@@ -173,15 +213,73 @@ ENDCLICOMMAND
 {
        [_contactManager removeDelegate: self];
        [_client release];
+       [_subRequests release];
        [_commands release];
        [super dealloc];
 }
 
+static JubCLIUI *completionData;
+static void completionCallback(OFString *buf, OFList *lc)
+{
+       if ([buf length] < 3)
+               return;
+
+       if (![buf hasPrefix: @":s "] && ![buf hasPrefix: @":m "] &&
+           ![buf hasPrefix: @":t "])
+               return;
+
+       if ([buf hasPrefix: @":t"]) {
+               OFString *options[] = {
+                       @":t available",
+                       @":t away",
+                       @":t dnd",
+                       @":t xa",
+                       @":t chat",
+                       @":t unavailable"
+               };
+
+               for (int i = 0; i < 6; i++) {
+                       if (![options[i] hasPrefix: buf])
+                               continue;
+                       [lc appendObject: options[i]];
+               }
+
+               return;
+       }
+
+       OFString *command = [buf substringWithRange: of_range(0, 3)];
+       OFString *query = [buf substringWithRange: of_range(3, [buf length]-3)];
+       OFDictionary *contacts = completionData.client.contactManager.contacts;
+       for (OFString *key in contacts) {
+               if (![key  hasPrefix: query])
+                       continue;
+               [lc appendObject: [command stringByAppendingString: key]];
+       }
+}
+
 - (void)startUIThread
 {
-       [of_stdin asyncReadLineWithTarget: self
-                                selector: @selector(Jub_userInputWithStream:
-                                                    line:exception:)];
+       completionData = self;
+
+       [[OFThread threadWithBlock: ^(void) {
+               OFString *line;
+               Linenoise *reader = [Linenoise sharedLinenoise];
+               reader.multiline = true;
+               reader.completionCallback = completionCallback;
+
+               while ((line = [reader readInputWithPrompt: @"> "]) != nil) @autoreleasepool {
+                       [self Jub_userInputWithStream: nil
+                                                line: line
+                                           exception: nil];
+                       if ([line length] != 0)
+                               [reader addHistoryItem: line];
+               }
+               [self Jub_userInputWithStream: nil
+                                        line: nil
+                                   exception: nil];
+
+               return nil;
+       }] start];
 }
 
 -      (void)client: (JubChatClient*)client
@@ -200,24 +298,26 @@ ENDCLICOMMAND
                      forKey: command.command];
 }
 
-- (BOOL)Jub_userInputWithStream: (OFStream*)stream
+- (bool)Jub_userInputWithStream: (OFStream*)stream
                           line: (OFString*)line
                      exception: (OFException*)exception
 {
-       if (line == nil)
+       if (line == nil || exception != nil)
                [OFApplication terminate];
 
        if ([line length] == 0)
-               return YES;
+               return true;
 
        if ([line characterAtIndex: 0] != ':') {
-               if (_sink == nil)
+               if (_sink == nil) {
                        [of_stdout writeLine: @"No default sink selected, "
                            @"type `:h` for help"];
+                       return true;
+               }
 
                [_sink send: line];
 
-               return YES;
+               return true;
        }
 
        line = [line stringByDeletingTrailingWhitespaces];
@@ -259,7 +359,7 @@ ENDCLICOMMAND
                        [of_stdout writeFormat: @"- %@\n", command.help];
                };
 
-               return YES;
+               return true;
        }
 
        id<JubCLICommand> command = _commands[input[0]];
@@ -268,12 +368,45 @@ ENDCLICOMMAND
                [command callWithParameters:
                    [input arrayByRemovingObject: [input firstObject]]];
 
-               return YES;
+               return true;
        }
 
        [of_stdout writeLine: @"Invalid command, type `:h` for help"];
 
-       return YES;
+       return true;
+}
+
+- (void)contactManager: (XMPPContactManager*)manager
+        didAddContact: (XMPPContact*)contact
+{
+       XMPPRosterItem *rosterItem = contact.rosterItem;
+       OFString *subscription = rosterItem.subscription;
+       OFString *bareJID = [rosterItem.JID bareJID];
+
+       if ([subscription isEqual: @"from"] || [subscription isEqual: @"both"])
+               [_subRequests removeObject: bareJID];
+}
+
+-          (void)contactManager: (XMPPContactManager*)manager
+  didReceiveSubscriptionRequest: (XMPPPresence*)presence
+{
+       OFString *bareJID = [presence.from bareJID];
+       [_subRequests addObject: bareJID];
+       [of_stdout writeFormat: @"\r" BOLD("%@") @" send a request to "
+           @"subscribe to your presence\r\n"
+           @"Use the :subs command to answer it\n",
+           bareJID];
+       [[Linenoise sharedLinenoise] refreshLine];
+}
+
+-            (void)contact: (XMPPContact*)contact
+  willUpdateWithRosterItem: (XMPPRosterItem*)rosterItem
+{
+       OFString *subscription = rosterItem.subscription;
+       OFString *bareJID = [rosterItem.JID bareJID];
+
+       if ([subscription isEqual: @"from"] || [subscription isEqual: @"both"])
+               [_subRequests removeObject: bareJID];
 }
 
 -  (void)contact: (XMPPContact*)contact
@@ -288,6 +421,7 @@ ENDCLICOMMAND
 -   (void)contact: (XMPPContact*)contact
   didSendPresence: (XMPPPresence*)presence
 {
+       [of_stdout writeFormat: @"\r"];
        [of_stdout writeFormat: BOLD("%@") @" is now in state ", presence.from];
 
        if ([presence.type isEqual: @"unavailable"])
@@ -307,5 +441,7 @@ ENDCLICOMMAND
                [of_stdout writeFormat: @": %@", presence.status];
 
        [of_stdout writeString: @"\n"];
+
+       [[Linenoise sharedLinenoise] refreshLine];
 }
 @end