1 /* BMailProtocol - the base class for protocol filters 2 ** 3 ** Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. 4 */ 5 6 7 #include <String.h> 8 #include <Alert.h> 9 #include <Query.h> 10 #include <VolumeRoster.h> 11 #include <StringList.h> 12 #include <E-mail.h> 13 #include <NodeInfo.h> 14 #include <Directory.h> 15 #include <NodeMonitor.h> 16 #include <Node.h> 17 18 #include <stdio.h> 19 #include <fs_attr.h> 20 #include <stdlib.h> 21 #include <assert.h> 22 23 #include <MDRLanguage.h> 24 25 class BMailProtocol; 26 27 #include <MailProtocol.h> 28 #include <ChainRunner.h> 29 #include <status.h> 30 31 namespace { 32 33 class ManifestAdder : public BMailChainCallback { 34 public: 35 ManifestAdder(BStringList *list,BStringList **list2, const char *id) : manifest(list), uids_on_disk(list2), uid(id) {} 36 virtual void Callback(status_t result) { 37 if (result == B_OK) { 38 (*manifest) += uid; 39 if (*uids_on_disk != NULL) 40 (**uids_on_disk) += uid; 41 } 42 } 43 44 private: 45 BStringList *manifest,**uids_on_disk; 46 const char *uid; 47 }; 48 49 class MessageDeletion : public BMailChainCallback { 50 public: 51 MessageDeletion(BMailProtocol *home, const char *uid, BEntry *io_entry, bool delete_anyway); 52 virtual void Callback(status_t result); 53 54 private: 55 BMailProtocol *us; 56 bool always; 57 const char *message_id; 58 BEntry *entry; 59 }; 60 61 } // unnamed namspace 62 63 inline void 64 BMailProtocol::error_alert(const char *process, status_t error) 65 { 66 BString string; 67 MDR_DIALECT_CHOICE ( 68 string << "Error while " << process << ": " << strerror(error); 69 runner->ShowError(string.String()); 70 , 71 string << process << "中にエラーが発生しました: " << strerror(error); 72 runner->ShowError(string.String()); 73 ) 74 } 75 76 77 namespace { 78 79 class DeleteHandler : public BHandler { 80 public: 81 DeleteHandler(BMailProtocol *a) 82 : us(a) 83 { 84 } 85 86 void MessageReceived(BMessage *msg) 87 { 88 if ((msg->what == 'DELE') && (us->InitCheck() == B_OK)) { 89 us->CheckForDeletedMessages(); 90 Looper()->RemoveHandler(this); 91 delete this; 92 } 93 } 94 95 private: 96 BMailProtocol *us; 97 }; 98 99 class TrashMonitor : public BHandler { 100 public: 101 TrashMonitor(BMailProtocol *a, int32 chain_id) 102 : us(a), trash("/boot/home/Desktop/Trash"), messages_for_us(0), id(chain_id) 103 { 104 } 105 106 void MessageReceived(BMessage *msg) 107 { 108 if (msg->what == 'INIT') { 109 node_ref to_watch; 110 trash.GetNodeRef(&to_watch); 111 watch_node(&to_watch,B_WATCH_DIRECTORY,this); 112 return; 113 } 114 if ((msg->what == B_NODE_MONITOR) && (us->InitCheck() == B_OK)) { 115 int32 opcode; 116 if (msg->FindInt32("opcode",&opcode) < B_OK) 117 return; 118 119 if (opcode == B_ENTRY_MOVED) { 120 int64 node(msg->FindInt64("to directory")); 121 dev_t device(msg->FindInt32("device")); 122 node_ref item_ref; 123 item_ref.node = node; 124 item_ref.device = device; 125 126 BDirectory moved_to(&item_ref); 127 128 BNode trash_item(&moved_to,msg->FindString("name")); 129 int32 chain; 130 if (trash_item.ReadAttr("MAIL:chain",B_INT32_TYPE,0,&chain,sizeof(chain)) < B_OK) 131 return; 132 133 if (chain == id) 134 messages_for_us += (moved_to == trash) ? 1 : -1; 135 } 136 137 if (messages_for_us < 0) 138 messages_for_us = 0; // Guard against weirdness 139 140 if (trash.CountEntries() == 0) { 141 if (messages_for_us > 0) 142 us->CheckForDeletedMessages(); 143 144 messages_for_us = 0; 145 } 146 } 147 } 148 149 private: 150 BMailProtocol *us; 151 BDirectory trash; 152 int32 messages_for_us; 153 int32 id; 154 }; 155 156 } // unnamed namspace 157 158 BMailProtocol::BMailProtocol(BMessage *settings, BMailChainRunner *run) 159 : BMailFilter(settings), 160 runner(run), trash_monitor(NULL), uids_on_disk(NULL) 161 { 162 unique_ids = new BStringList; 163 BMailProtocol::settings = settings; 164 165 manifest = new BStringList; 166 167 { 168 BString attr_name = "MAIL:"; 169 attr_name << runner->Chain()->ID() << ":manifest"; //--- In case someone puts multiple accounts in the same directory 170 171 if (runner->Chain()->MetaData()->HasString("path")) { 172 BNode node(runner->Chain()->MetaData()->FindString("path")); 173 if (node.InitCheck() >= B_OK) { 174 attr_info info; 175 if (node.GetAttrInfo(attr_name.String(),&info) < B_OK) { 176 if (runner->Chain()->MetaData()->FindFlat("manifest", manifest) == B_OK) { 177 runner->Chain()->MetaData()->RemoveName("manifest"); 178 runner->Chain()->Save(); //--- Not having this code made an earlier version of MDR delete all my *(&(*& mail 179 } 180 } else { 181 void *flatmanifest = malloc(info.size); 182 node.ReadAttr(attr_name.String(),manifest->TypeCode(),0,flatmanifest,info.size); 183 manifest->Unflatten(manifest->TypeCode(),flatmanifest,info.size); 184 free(flatmanifest); 185 } 186 } else runner->ShowError("Error while reading account manifest: cannot use destination directory."); 187 } else runner->ShowError("Error while reading account manifest: no destination directory exists."); 188 } 189 190 uids_on_disk = new BStringList; 191 BVolumeRoster volumes; 192 BVolume volume; 193 while (volumes.GetNextVolume(&volume) == B_OK) { 194 BQuery fido; 195 entry_ref entry; 196 197 fido.SetVolume(&volume); 198 fido.PushAttr("MAIL:chain"); 199 fido.PushInt32(settings->FindInt32("chain")); 200 fido.PushOp(B_EQ); 201 fido.PushAttr("MAIL:pending_chain"); 202 fido.PushInt32(settings->FindInt32("chain")); 203 fido.PushOp(B_EQ); 204 fido.PushOp(B_OR); 205 if (!settings->FindBool("leave_mail_on_server")) { 206 fido.PushAttr("BEOS:type"); 207 fido.PushString("text/x-partial-email"); 208 fido.PushOp(B_EQ); 209 fido.PushOp(B_AND); 210 } 211 fido.Fetch(); 212 213 BString uid; 214 while (fido.GetNextRef(&entry) == B_OK) { 215 BNode(&entry).ReadAttrString("MAIL:unique_id",&uid); 216 uids_on_disk->AddItem(uid.String()); 217 } 218 } 219 220 (*manifest) |= (*uids_on_disk); 221 222 if (!settings->FindBool("login_and_do_nothing_else_of_any_importance")) { 223 DeleteHandler *h = new DeleteHandler(this); 224 runner->AddHandler(h); 225 runner->PostMessage('DELE',h); 226 227 trash_monitor = new TrashMonitor(this,runner->Chain()->ID()); 228 runner->AddHandler(trash_monitor); 229 runner->PostMessage('INIT',trash_monitor); 230 } 231 } 232 233 234 BMailProtocol::~BMailProtocol() 235 { 236 if (manifest != NULL) { 237 BMessage *meta_data = runner->Chain()->MetaData(); 238 meta_data->RemoveName("manifest"); 239 BString attr_name = "MAIL:"; 240 attr_name << runner->Chain()->ID() << ":manifest"; //--- In case someone puts multiple accounts in the same directory 241 if (meta_data->HasString("path")) { 242 BNode node(meta_data->FindString("path")); 243 if (node.InitCheck() >= B_OK) { 244 node.RemoveAttr(attr_name.String()); 245 ssize_t manifestsize = manifest->FlattenedSize(); 246 void *flatmanifest = malloc(manifestsize); 247 manifest->Flatten(flatmanifest,manifestsize); 248 if (status_t err = node.WriteAttr(attr_name.String(),manifest->TypeCode(),0,flatmanifest,manifestsize) < B_OK) { 249 BString error = "Error while saving account manifest: "; 250 error << strerror(err); 251 runner->ShowError(error.String()); 252 } 253 free(flatmanifest); 254 } else runner->ShowError("Error while saving account manifest: cannot use destination directory."); 255 } else runner->ShowError("Error while saving account manifest: no destination directory exists."); 256 } 257 delete unique_ids; 258 delete manifest; 259 delete trash_monitor; 260 delete uids_on_disk; 261 } 262 263 264 #define dump_stringlist(a) printf("BStringList %s:\n",#a); \ 265 for (int32 i = 0; i < (a)->CountItems(); i++)\ 266 puts((a)->ItemAt(i)); \ 267 puts("Done\n"); 268 269 status_t 270 BMailProtocol::ProcessMailMessage(BPositionIO **io_message, BEntry *io_entry, 271 BMessage *io_headers, BPath *io_folder, const char *io_uid) 272 { 273 status_t error; 274 275 if (io_uid == NULL) 276 return B_ERROR; 277 278 error = GetMessage(io_uid, io_message, io_headers, io_folder); 279 if (error < B_OK) { 280 if (error != B_MAIL_END_FETCH) { 281 MDR_DIALECT_CHOICE ( 282 error_alert("getting a message",error);, 283 error_alert("新しいメッセージヲ取得中にエラーが発生しました",error); 284 ); 285 } 286 return B_MAIL_END_FETCH; 287 } 288 289 runner->RegisterMessageCallback(new ManifestAdder(manifest, &uids_on_disk, io_uid)); 290 runner->RegisterMessageCallback(new MessageDeletion(this, io_uid, io_entry, !settings->FindBool("leave_mail_on_server"))); 291 292 return B_OK; 293 } 294 295 void BMailProtocol::CheckForDeletedMessages() { 296 { 297 //---Delete things from the manifest no longer on the server 298 BStringList temp; 299 manifest->NotThere(*unique_ids, &temp); 300 (*manifest) -= temp; 301 } 302 303 if (((settings->FindBool("delete_remote_when_local")) || !(settings->FindBool("leave_mail_on_server"))) && (manifest->CountItems() > 0)) { 304 BStringList to_delete; 305 306 if (uids_on_disk == NULL) { 307 BStringList query_contents; 308 BVolumeRoster volumes; 309 BVolume volume; 310 311 while (volumes.GetNextVolume(&volume) == B_OK) { 312 BQuery fido; 313 entry_ref entry; 314 315 fido.SetVolume(&volume); 316 fido.PushAttr("MAIL:chain"); 317 fido.PushInt32(settings->FindInt32("chain")); 318 fido.PushOp(B_EQ); 319 fido.PushAttr("MAIL:pending_chain"); 320 fido.PushInt32(settings->FindInt32("chain")); 321 fido.PushOp(B_EQ); 322 fido.PushOp(B_OR); 323 fido.Fetch(); 324 325 BString uid; 326 while (fido.GetNextRef(&entry) == B_OK) { 327 BNode(&entry).ReadAttrString("MAIL:unique_id",&uid); 328 query_contents.AddItem(uid.String()); 329 } 330 } 331 332 query_contents.NotHere(*manifest,&to_delete); 333 } else { 334 uids_on_disk->NotHere(*manifest,&to_delete); 335 delete uids_on_disk; 336 uids_on_disk = NULL; 337 } 338 339 for (int32 i = 0; i < to_delete.CountItems(); i++) 340 DeleteMessage(to_delete[i]); 341 342 //*(unique_ids) -= to_delete; --- This line causes bad things to 343 // happen (POP3 client uses the wrong indices to retrieve 344 // messages). Without it, bad things don't happen. 345 *(manifest) -= to_delete; 346 } 347 } 348 349 void BMailProtocol::_ReservedProtocol1() {} 350 void BMailProtocol::_ReservedProtocol2() {} 351 void BMailProtocol::_ReservedProtocol3() {} 352 void BMailProtocol::_ReservedProtocol4() {} 353 void BMailProtocol::_ReservedProtocol5() {} 354 355 356 // #pragma mark - 357 358 359 MessageDeletion::MessageDeletion(BMailProtocol *home, const char *uid, 360 BEntry *io_entry, bool delete_anyway) 361 : 362 us(home), 363 always(delete_anyway), 364 message_id(uid), entry(io_entry) 365 { 366 } 367 368 369 void 370 MessageDeletion::Callback(status_t result) 371 { 372 #if DEBUG 373 printf("Deleting %s\n", message_id); 374 #endif 375 BNode node(entry); 376 BNodeInfo info(&node); 377 char type[255]; 378 info.GetType(type); 379 if ((always && strcmp(B_MAIL_TYPE,type) == 0) || result == B_MAIL_DISCARD) 380 us->DeleteMessage(message_id); 381 } 382