1 /* 2 * Copyright 2010, Haiku, Inc. All rights reserved. 3 * Copyright 2008, Pier Luigi Fiorini. 4 * Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> 8 * Stephan Aßmus <superstippi@gmx.de> 9 */ 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <Application.h> 16 #include <Bitmap.h> 17 #include <IconUtils.h> 18 #include <List.h> 19 #include <Mime.h> 20 #include <Notification.h> 21 #include <Path.h> 22 #include <Roster.h> 23 #include <TranslationUtils.h> 24 25 const char* kSignature = "application/x-vnd.Haiku-notify"; 26 const char* kSmallIconAttribute = "BEOS:M:STD_ICON"; 27 const char* kLargeIconAttribute = "BEOS:L:STD_ICON"; 28 const char* kIconAttribute = "BEOS:ICON"; 29 30 const char *kTypeNames[] = { 31 "information", 32 "important", 33 "error", 34 "progress", 35 NULL 36 }; 37 38 const int32 kErrorInitFail = 127; 39 const int32 kErrorArgumentsFail = 126; 40 41 class NotifyApp : public BApplication { 42 public: 43 NotifyApp(); 44 virtual ~NotifyApp(); 45 46 virtual void ReadyToRun(); 47 virtual void ArgvReceived(int32 argc, char** argv); 48 49 bool HasGoodArguments() const 50 { return fHasGoodArguments; } 51 52 private: 53 bool fHasGoodArguments; 54 notification_type fType; 55 char* fAppName; 56 char* fTitle; 57 char* fMsgId; 58 float fProgress; 59 bigtime_t fTimeout; 60 char* fIconFile; 61 entry_ref fFileRef; 62 char* fMessage; 63 char* fApp; 64 bool fHasFile; 65 entry_ref fFile; 66 BList* fRefs; 67 BList* fArgv; 68 69 void _Usage() const; 70 BBitmap* _GetBitmap(const entry_ref* ref) const; 71 }; 72 73 74 NotifyApp::NotifyApp() 75 : 76 BApplication(kSignature), 77 fHasGoodArguments(false), 78 fType(B_INFORMATION_NOTIFICATION), 79 fAppName(NULL), 80 fTitle(NULL), 81 fMsgId(NULL), 82 fProgress(0.0f), 83 fTimeout(0), 84 fIconFile(NULL), 85 fMessage(NULL), 86 fApp(NULL), 87 fHasFile(false) 88 { 89 fRefs = new BList(); 90 fArgv = new BList(); 91 } 92 93 94 NotifyApp::~NotifyApp() 95 { 96 free(fAppName); 97 free(fTitle); 98 free(fMsgId); 99 free(fIconFile); 100 free(fMessage); 101 free(fApp); 102 103 for (int32 i = 0; void* item = fRefs->ItemAt(i); i++) 104 delete (BEntry*)item; 105 delete fRefs; 106 107 for (int32 i = 0; void* item = fArgv->ItemAt(i); i++) 108 free(item); 109 delete fArgv; 110 } 111 112 113 void 114 NotifyApp::ArgvReceived(int32 argc, char** argv) 115 { 116 const uint32 kArgCount = argc - 1; 117 uint32 index = 1; 118 119 // Look for valid options 120 for (; index <= kArgCount; ++index) { 121 if (argv[index][0] == '-' && argv[index][1] == '-') { 122 const char* option = argv[index] + 2; 123 124 if (++index > kArgCount) { 125 // No argument to option 126 fprintf(stderr, "Missing argument to option --%s\n\n", option); 127 return; 128 } 129 130 const char* argument = argv[index]; 131 132 if (strcmp(option, "type") == 0) { 133 for (int32 i = 0; kTypeNames[i]; i++) { 134 if (strncmp(kTypeNames[i], argument, strlen(argument)) == 0) 135 fType = (notification_type)i; 136 } 137 } else if (strcmp(option, "app") == 0) 138 fAppName = strdup(argument); 139 else if (strcmp(option, "title") == 0) 140 fTitle = strdup(argument); 141 else if (strcmp(option, "messageID") == 0) 142 fMsgId = strdup(argument); 143 else if (strcmp(option, "progress") == 0) 144 fProgress = atof(argument); 145 else if (strcmp(option, "timeout") == 0) 146 fTimeout = atol(argument) * 1000000; 147 else if (strcmp(option, "icon") == 0) { 148 fIconFile = strdup(argument); 149 150 if (get_ref_for_path(fIconFile, &fFileRef) < B_OK) { 151 fprintf(stderr, "Bad icon path!\n\n"); 152 return; 153 } 154 } else if (strcmp(option, "onClickApp") == 0) 155 fApp = strdup(argument); 156 else if (strcmp(option, "onClickFile") == 0) { 157 if (get_ref_for_path(argument, &fFile) != B_OK) { 158 fprintf(stderr, "Bad path for --onClickFile!\n\n"); 159 return; 160 } 161 162 fHasFile = true; 163 } else if (strcmp(option, "onClickRef") == 0) { 164 entry_ref ref; 165 166 if (get_ref_for_path(argument, &ref) != B_OK) { 167 fprintf(stderr, "Bad path for --onClickRef!\n\n"); 168 return; 169 } 170 171 fRefs->AddItem(new BEntry(&ref)); 172 } else if (strcmp(option, "onClickArgv") == 0) 173 fArgv->AddItem(strdup(argument)); 174 else { 175 // Unrecognized option 176 fprintf(stderr, "Unrecognized option --%s\n\n", option); 177 return; 178 } 179 } else { 180 // Option doesn't start with '--' 181 break; 182 } 183 184 if (index == kArgCount) { 185 // No text argument provided, only '--' arguments 186 fprintf(stderr, "Missing message argument!\n\n"); 187 return; 188 } 189 } 190 191 // Check for missing arguments 192 if (fAppName == NULL) { 193 fprintf(stderr, "Missing --app argument!\n\n"); 194 return; 195 } 196 if (fTitle == NULL) { 197 fprintf(stderr, "Missing --title argument!\n\n"); 198 return; 199 } 200 201 fMessage = strdup(argv[index]); 202 fHasGoodArguments = true; 203 } 204 205 void 206 NotifyApp::_Usage() const 207 { 208 fprintf(stderr, "Usage: notify [OPTION]... [MESSAGE]\n" 209 "Send notifications to notification_server.\n" 210 " --type <type>\tNotification type,\n" 211 " \t <type>: "); 212 213 for (int32 i = 0; kTypeNames[i]; i++) 214 fprintf(stderr, kTypeNames[i + 1] ? "%s|" : "%s\n", kTypeNames[i]); 215 216 fprintf(stderr, 217 " --app <app name>\tApplication name\n" 218 " --title <title>\tMessage title\n" 219 " --messageID <msg id>\tMessage ID\n" 220 " --progress <float>\tProgress, value between 0.0 and 1.0 - if type is set to progress\n" 221 " --timeout <secs>\tSpecify timeout\n" 222 " --onClickApp <signature>\tApplication to open when notification is clicked\n" 223 " --onClickFile <fullpath>\tFile to open when notification is clicked\n" 224 " --onClickRef <fullpath>\tFile to open with the application when notification is clicked\n" 225 " --onClickArgv <arg>\tArgument to the application when notification is clicked\n" 226 " --icon <icon file> Icon\n"); 227 } 228 229 230 BBitmap* 231 NotifyApp::_GetBitmap(const entry_ref* ref) const 232 { 233 BBitmap* bitmap = NULL; 234 235 // First try by contents 236 bitmap = BTranslationUtils::GetBitmap(ref); 237 if (bitmap) 238 return bitmap; 239 240 // Then, try reading its attribute 241 BNode node(BPath(ref).Path()); 242 bitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1, 243 (float)B_LARGE_ICON - 1), B_RGBA32); 244 if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute, 245 kLargeIconAttribute, B_LARGE_ICON, bitmap) != B_OK) { 246 delete bitmap; 247 bitmap = NULL; 248 } 249 250 return bitmap; 251 } 252 253 254 void 255 NotifyApp::ReadyToRun() 256 { 257 if (HasGoodArguments()) { 258 BNotification notification(fType); 259 notification.SetApplication(fAppName); 260 notification.SetTitle(fTitle); 261 notification.SetContent(fMessage); 262 263 if (fMsgId != NULL) 264 notification.SetMessageID(fMsgId); 265 266 if (fType == B_PROGRESS_NOTIFICATION) 267 notification.SetProgress(fProgress); 268 269 if (fIconFile != NULL) { 270 BBitmap* bitmap = _GetBitmap(&fFileRef); 271 if (bitmap) { 272 notification.SetIcon(bitmap); 273 delete bitmap; 274 } 275 } 276 277 if (fApp != NULL) 278 notification.SetOnClickApp(fApp); 279 280 if (fHasFile) 281 notification.SetOnClickFile(&fFile); 282 283 for (int32 i = 0; void* item = fRefs->ItemAt(i); i++) { 284 BEntry* entry = (BEntry*)item; 285 286 entry_ref ref; 287 if (entry->GetRef(&ref) == B_OK) 288 notification.AddOnClickRef(&ref); 289 } 290 291 for (int32 i = 0; void* item = fArgv->ItemAt(i); i++) { 292 const char* arg = (const char*)item; 293 notification.AddOnClickArg(arg); 294 } 295 296 status_t ret = be_roster->Notify(notification, fTimeout); 297 if (ret != B_OK) { 298 fprintf(stderr, "Failed to deliver notification: %s\n", 299 strerror(ret)); 300 } 301 } else 302 _Usage(); 303 304 Quit(); 305 } 306 307 308 int 309 main(int argc, char** argv) 310 { 311 NotifyApp app; 312 if (app.InitCheck() != B_OK) 313 return kErrorInitFail; 314 315 app.Run(); 316 if (!app.HasGoodArguments()) 317 return kErrorArgumentsFail; 318 319 return 0; 320 } 321