1 /* 2 * Copyright 2011-2013, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <stdlib.h> 8 9 #include "Protocol.h" 10 #include "Response.h" 11 12 #include "argv.h" 13 14 15 struct cmd_entry { 16 const char* name; 17 void (*func)(int argc, char **argv); 18 const char* help; 19 }; 20 21 22 static void do_help(int argc, char** argv); 23 24 25 extern const char* __progname; 26 static const char* kProgramName = __progname; 27 28 static IMAP::Protocol sProtocol; 29 30 31 static void 32 error(const char* context, status_t status) 33 { 34 fprintf(stderr, "Error during %s: %s\n", context, strerror(status)); 35 } 36 37 38 static void 39 usage() 40 { 41 printf("Usage: %s <server> <username> <password>\n", kProgramName); 42 exit(1); 43 } 44 45 46 // #pragma mark - 47 48 49 static void 50 do_select(int argc, char** argv) 51 { 52 const char* folder = "INBOX"; 53 if (argc > 1) 54 folder = argv[1]; 55 56 IMAP::SelectCommand command(folder); 57 status_t status = sProtocol.ProcessCommand(command); 58 if (status == B_OK) { 59 printf("Next UID: %" B_PRIu32 ", UID validity: %" B_PRIu32 "\n", 60 command.NextUID(), command.UIDValidity()); 61 } else 62 error("select", status); 63 } 64 65 66 static void 67 do_folders(int argc, char** argv) 68 { 69 IMAP::FolderList folders; 70 BString separator; 71 72 status_t status = sProtocol.GetFolders(folders, separator); 73 if (status != B_OK) { 74 error("folders", status); 75 return; 76 } 77 78 for (size_t i = 0; i < folders.size(); i++) { 79 printf(" %s %s\n", folders[i].subscribed ? "*" : " ", 80 folders[i].folder.String()); 81 } 82 } 83 84 85 static void 86 do_fetch(int argc, char** argv) 87 { 88 uint32 from = 1; 89 uint32 to; 90 uint32 flags = IMAP::kFetchAll; 91 if (argc < 2) { 92 printf("usage: %s [<from>] [<to>] [header|body]\n", argv[0]); 93 return; 94 } 95 if (argc > 2) { 96 if (!strcasecmp(argv[argc - 1], "header")) { 97 flags = IMAP::kFetchHeader; 98 argc--; 99 } else if (!strcasecmp(argv[argc - 1], "body")) { 100 flags = IMAP::kFetchBody; 101 argc--; 102 } 103 } 104 if (argc > 2) { 105 from = atoul(argv[1]); 106 to = atoul(argv[2]); 107 } else 108 from = to = atoul(argv[1]); 109 110 IMAP::FetchCommand command(from, to, flags | IMAP::kFetchFlags); 111 112 // A fetch listener that dumps everything to stdout 113 class Listener : public IMAP::FetchListener { 114 public: 115 virtual ~Listener() 116 { 117 } 118 119 virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, 120 size_t& length) 121 { 122 fBuffer.SetSize(0); 123 124 char buffer[65535]; 125 while (length > 0) { 126 ssize_t bytesRead = stream.Read(buffer, 127 min_c(sizeof(buffer), length)); 128 if (bytesRead <= 0) 129 break; 130 131 fBuffer.Write(buffer, bytesRead); 132 length -= bytesRead; 133 } 134 135 // Null terminate the buffer 136 char null = '\0'; 137 fBuffer.Write(&null, 1); 138 139 return true; 140 } 141 142 virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags) 143 { 144 printf("================= UID %ld, flags %lx =================\n", 145 uid, flags); 146 puts((const char*)fBuffer.Buffer()); 147 } 148 149 private: 150 BMallocIO fBuffer; 151 } listener; 152 153 command.SetListener(&listener); 154 155 status_t status = sProtocol.ProcessCommand(command); 156 if (status != B_OK) { 157 error("fetch", status); 158 return; 159 } 160 } 161 162 163 static void 164 do_flags(int argc, char** argv) 165 { 166 uint32 from = 1; 167 uint32 to; 168 if (argc < 2) { 169 printf("usage: %s [<from>] [<to>]\n", argv[0]); 170 return; 171 } 172 if (argc > 2) { 173 from = atoul(argv[1]); 174 to = atoul(argv[2]); 175 } else 176 to = atoul(argv[1]); 177 178 IMAP::MessageEntryList entries; 179 IMAP::FetchMessageEntriesCommand command(entries, from, to, true); 180 status_t status = sProtocol.ProcessCommand(command); 181 if (status != B_OK) { 182 error("flags", status); 183 return; 184 } 185 186 for (size_t i = 0; i < entries.size(); i++) { 187 printf("%10lu %8lu bytes, flags: %#lx\n", entries[i].uid, 188 entries[i].size, entries[i].flags); 189 } 190 } 191 192 193 static void 194 do_noop(int argc, char** argv) 195 { 196 IMAP::RawCommand command("NOOP"); 197 status_t status = sProtocol.ProcessCommand(command); 198 if (status != B_OK) 199 error("noop", status); 200 } 201 202 203 static void 204 do_raw(int argc, char** argv) 205 { 206 // build command back again 207 char command[4096]; 208 command[0] = '\0'; 209 210 for (int i = 1; i < argc; i++) { 211 if (i > 1) 212 strlcat(command, " ", sizeof(command)); 213 strlcat(command, argv[i], sizeof(command)); 214 } 215 216 class RawCommand : public IMAP::Command, public IMAP::Handler { 217 public: 218 RawCommand(const char* command) 219 : 220 fCommand(command) 221 { 222 } 223 224 BString CommandString() 225 { 226 return fCommand; 227 } 228 229 bool HandleUntagged(IMAP::Response& response) 230 { 231 if (response.IsCommand(fCommand)) { 232 printf("-> %s\n", response.ToString().String()); 233 return true; 234 } 235 236 return false; 237 } 238 239 private: 240 const char* fCommand; 241 }; 242 RawCommand rawCommand(command); 243 status_t status = sProtocol.ProcessCommand(rawCommand); 244 if (status != B_OK) 245 error("raw", status); 246 } 247 248 249 static cmd_entry sBuiltinCommands[] = { 250 {"select", do_select, "Selects a mailbox, defaults to INBOX"}, 251 {"folders", do_folders, "List of existing folders"}, 252 {"flags", do_flags, 253 "List of all mail UIDs in the mailbox with their flags"}, 254 {"fetch", do_fetch, 255 "Fetch mails via UIDs"}, 256 {"noop", do_noop, "Issue a NOOP command (will report new messages)"}, 257 {"raw", do_raw, "Issue a raw command to the server"}, 258 {"help", do_help, "prints this help text"}, 259 {"quit", NULL, "exits the application"}, 260 {NULL, NULL, NULL}, 261 }; 262 263 264 static void 265 do_help(int argc, char** argv) 266 { 267 printf("Available commands:\n"); 268 269 for (cmd_entry* command = sBuiltinCommands; command->name != NULL; 270 command++) { 271 printf("%8s - %s\n", command->name, command->help); 272 } 273 } 274 275 276 // #pragma mark - 277 278 279 int 280 main(int argc, char** argv) 281 { 282 if (argc < 4) 283 usage(); 284 285 const char* server = argv[1]; 286 const char* user = argv[2]; 287 const char* password = argv[3]; 288 bool useSSL = argc > 4; 289 uint16 port = useSSL ? 993 : 143; 290 291 BNetworkAddress address(AF_INET, server, port); 292 printf("Connecting to \"%s\" as %s%s, port %u\n", server, user, 293 useSSL ? " with SSL" : "", address.Port()); 294 295 status_t status = sProtocol.Connect(address, user, password, useSSL); 296 if (status != B_OK) { 297 error("connect", status); 298 return 1; 299 } 300 301 while (true) { 302 printf("> "); 303 fflush(stdout); 304 305 char line[1024]; 306 if (fgets(line, sizeof(line), stdin) == NULL) 307 break; 308 309 argc = 0; 310 argv = build_argv(line, &argc); 311 if (argv == NULL || argc == 0) 312 continue; 313 314 if (!strcmp(argv[0], "quit") 315 || !strcmp(argv[0], "exit") 316 || !strcmp(argv[0], "q")) 317 break; 318 319 int length = strlen(argv[0]); 320 bool found = false; 321 322 for (cmd_entry* command = sBuiltinCommands; command->name != NULL; 323 command++) { 324 if (!strncmp(command->name, argv[0], length)) { 325 command->func(argc, argv); 326 found = true; 327 break; 328 } 329 } 330 331 if (!found) { 332 fprintf(stderr, "Unknown command \"%s\". Type \"help\" for a " 333 "list of commands.\n", argv[0]); 334 } 335 336 free(argv); 337 } 338 339 340 return 0; 341 } 342