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