##// END OF EJS Templates
fixes #481 rhodecode emails are sent without Date header...
marcink -
r2452:d95ef658 beta
parent child Browse files
Show More
@@ -1,699 +1,700 b''
1 1 .. _changelog:
2 2
3 3 =========
4 4 Changelog
5 5 =========
6 6
7 7 1.4.0 (**2012-XX-XX**)
8 8 ----------------------
9 9
10 10 :status: in-progress
11 11 :branch: beta
12 12
13 13 news
14 14 ++++
15 15
16 16 - new codereview system
17 17 - email map, allowing users to have multiple email addresses mapped into
18 18 their accounts
19 19 - improved git-hook system. Now all actions for git are logged into journal
20 20 including pushed revisions, user and IP address
21 21 - changed setup-app into setup-rhodecode and added default options to it.
22 22 - new git repos are created as bare now by default
23 23 - #464 added links to groups in permission box
24 24 - #465 mentions autocomplete inside comments boxes
25 25 - #469 added --update-only option to whoosh to re-index only given list
26 26 of repos in index
27 27 - rhodecode-api CLI client
28 28 - new git http protocol replaced buggy dulwich implementation.
29 29 Now based on pygrack & gitweb
30 30 - Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
31 31 reformated based on user suggestions. Additional rss/atom feeds for user
32 32 journal
33 33 - various i18n improvements
34 34 - #478 permissions overview for admin in user edit view
35 35
36 36 fixes
37 37 +++++
38 38
39 39 - improved translations
40 40 - fixes issue #455 Creating an archive generates an exception on Windows
41 41 - fixes #448 Download ZIP archive keeps file in /tmp open and results
42 42 in out of disk space
43 43 - fixes issue #454 Search results under Windows include proceeding
44 44 backslash
45 45 - fixed issue #450. Rhodecode no longer will crash when bad revision is
46 46 present in journal data.
47 47 - fix for issue #417, git execution was broken on windows for certain
48 48 commands.
49 49 - fixed #413. Don't disable .git directory for bare repos on deleting
50 50 - fixed issue #459. Changed the way of obtaining logger in reindex task.
51 51 - fixed #453 added ID field in whoosh SCHEMA that solves the issue of
52 52 reindexing modified files
53 - fixes #481 rhodecode emails are sent without Date header
53 54
54 55 1.3.6 (**2012-05-17**)
55 56 ----------------------
56 57
57 58 news
58 59 ++++
59 60
60 61 - chinese traditional translation
61 62 - changed setup-app into setup-rhodecode and added arguments for auto-setup
62 63 mode that doesn't need user interaction
63 64
64 65 fixes
65 66 +++++
66 67
67 68 - fixed no scm found warning
68 69 - fixed __future__ import error on rcextensions
69 70 - made simplejson required lib for speedup on JSON encoding
70 71 - fixes #449 bad regex could get more than revisions from parsing history
71 72 - don't clear DB session when CELERY_EAGER is turned ON
72 73
73 74 1.3.5 (**2012-05-10**)
74 75 ----------------------
75 76
76 77 news
77 78 ++++
78 79
79 80 - use ext_json for json module
80 81 - unified annotation view with file source view
81 82 - notification improvements, better inbox + css
82 83 - #419 don't strip passwords for login forms, make rhodecode
83 84 more compatible with LDAP servers
84 85 - Added HTTP_X_FORWARDED_FOR as another method of extracting
85 86 IP for pull/push logs. - moved all to base controller
86 87 - #415: Adding comment to changeset causes reload.
87 88 Comments are now added via ajax and doesn't reload the page
88 89 - #374 LDAP config is discarded when LDAP can't be activated
89 90 - limited push/pull operations are now logged for git in the journal
90 91 - bumped mercurial to 2.2.X series
91 92 - added support for displaying submodules in file-browser
92 93 - #421 added bookmarks in changelog view
93 94
94 95 fixes
95 96 +++++
96 97
97 98 - fixed dev-version marker for stable when served from source codes
98 99 - fixed missing permission checks on show forks page
99 100 - #418 cast to unicode fixes in notification objects
100 101 - #426 fixed mention extracting regex
101 102 - fixed remote-pulling for git remotes remopositories
102 103 - fixed #434: Error when accessing files or changesets of a git repository
103 104 with submodules
104 105 - fixed issue with empty APIKEYS for users after registration ref. #438
105 106 - fixed issue with getting README files from git repositories
106 107
107 108 1.3.4 (**2012-03-28**)
108 109 ----------------------
109 110
110 111 news
111 112 ++++
112 113
113 114 - Whoosh logging is now controlled by the .ini files logging setup
114 115 - added clone-url into edit form on /settings page
115 116 - added help text into repo add/edit forms
116 117 - created rcextensions module with additional mappings (ref #322) and
117 118 post push/pull/create repo hooks callbacks
118 119 - implemented #377 Users view for his own permissions on account page
119 120 - #399 added inheritance of permissions for users group on repos groups
120 121 - #401 repository group is automatically pre-selected when adding repos
121 122 inside a repository group
122 123 - added alternative HTTP 403 response when client failed to authenticate. Helps
123 124 solving issues with Mercurial and LDAP
124 125 - #402 removed group prefix from repository name when listing repositories
125 126 inside a group
126 127 - added gravatars into permission view and permissions autocomplete
127 128 - #347 when running multiple RhodeCode instances, properly invalidates cache
128 129 for all registered servers
129 130
130 131 fixes
131 132 +++++
132 133
133 134 - fixed #390 cache invalidation problems on repos inside group
134 135 - fixed #385 clone by ID url was loosing proxy prefix in URL
135 136 - fixed some unicode problems with waitress
136 137 - fixed issue with escaping < and > in changeset commits
137 138 - fixed error occurring during recursive group creation in API
138 139 create_repo function
139 140 - fixed #393 py2.5 fixes for routes url generator
140 141 - fixed #397 Private repository groups shows up before login
141 142 - fixed #396 fixed problems with revoking users in nested groups
142 143 - fixed mysql unicode issues + specified InnoDB as default engine with
143 144 utf8 charset
144 145 - #406 trim long branch/tag names in changelog to not break UI
145 146
146 147 1.3.3 (**2012-03-02**)
147 148 ----------------------
148 149
149 150 news
150 151 ++++
151 152
152 153
153 154 fixes
154 155 +++++
155 156
156 157 - fixed some python2.5 compatibility issues
157 158 - fixed issues with removed repos was accidentally added as groups, after
158 159 full rescan of paths
159 160 - fixes #376 Cannot edit user (using container auth)
160 161 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
161 162 configuration
162 163 - fixed initial sorting of repos inside repo group
163 164 - fixes issue when user tried to resubmit same permission into user/user_groups
164 165 - bumped beaker version that fixes #375 leap error bug
165 166 - fixed raw_changeset for git. It was generated with hg patch headers
166 167 - fixed vcs issue with last_changeset for filenodes
167 168 - fixed missing commit after hook delete
168 169 - fixed #372 issues with git operation detection that caused a security issue
169 170 for git repos
170 171
171 172 1.3.2 (**2012-02-28**)
172 173 ----------------------
173 174
174 175 news
175 176 ++++
176 177
177 178
178 179 fixes
179 180 +++++
180 181
181 182 - fixed git protocol issues with repos-groups
182 183 - fixed git remote repos validator that prevented from cloning remote git repos
183 184 - fixes #370 ending slashes fixes for repo and groups
184 185 - fixes #368 improved git-protocol detection to handle other clients
185 186 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
186 187 Moved To Root
187 188 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
188 189 - fixed #373 missing cascade drop on user_group_to_perm table
189 190
190 191 1.3.1 (**2012-02-27**)
191 192 ----------------------
192 193
193 194 news
194 195 ++++
195 196
196 197
197 198 fixes
198 199 +++++
199 200
200 201 - redirection loop occurs when remember-me wasn't checked during login
201 202 - fixes issues with git blob history generation
202 203 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
203 204
204 205 1.3.0 (**2012-02-26**)
205 206 ----------------------
206 207
207 208 news
208 209 ++++
209 210
210 211 - code review, inspired by github code-comments
211 212 - #215 rst and markdown README files support
212 213 - #252 Container-based and proxy pass-through authentication support
213 214 - #44 branch browser. Filtering of changelog by branches
214 215 - mercurial bookmarks support
215 216 - new hover top menu, optimized to add maximum size for important views
216 217 - configurable clone url template with possibility to specify protocol like
217 218 ssh:// or http:// and also manually alter other parts of clone_url.
218 219 - enabled largefiles extension by default
219 220 - optimized summary file pages and saved a lot of unused space in them
220 221 - #239 option to manually mark repository as fork
221 222 - #320 mapping of commit authors to RhodeCode users
222 223 - #304 hashes are displayed using monospace font
223 224 - diff configuration, toggle white lines and context lines
224 225 - #307 configurable diffs, whitespace toggle, increasing context lines
225 226 - sorting on branches, tags and bookmarks using YUI datatable
226 227 - improved file filter on files page
227 228 - implements #330 api method for listing nodes ar particular revision
228 229 - #73 added linking issues in commit messages to chosen issue tracker url
229 230 based on user defined regular expression
230 231 - added linking of changesets in commit messages
231 232 - new compact changelog with expandable commit messages
232 233 - firstname and lastname are optional in user creation
233 234 - #348 added post-create repository hook
234 235 - #212 global encoding settings is now configurable from .ini files
235 236 - #227 added repository groups permissions
236 237 - markdown gets codehilite extensions
237 238 - new API methods, delete_repositories, grante/revoke permissions for groups
238 239 and repos
239 240
240 241
241 242 fixes
242 243 +++++
243 244
244 245 - rewrote dbsession management for atomic operations, and better error handling
245 246 - fixed sorting of repo tables
246 247 - #326 escape of special html entities in diffs
247 248 - normalized user_name => username in api attributes
248 249 - fixes #298 ldap created users with mixed case emails created conflicts
249 250 on saving a form
250 251 - fixes issue when owner of a repo couldn't revoke permissions for users
251 252 and groups
252 253 - fixes #271 rare JSON serialization problem with statistics
253 254 - fixes #337 missing validation check for conflicting names of a group with a
254 255 repositories group
255 256 - #340 fixed session problem for mysql and celery tasks
256 257 - fixed #331 RhodeCode mangles repository names if the a repository group
257 258 contains the "full path" to the repositories
258 259 - #355 RhodeCode doesn't store encrypted LDAP passwords
259 260
260 261 1.2.5 (**2012-01-28**)
261 262 ----------------------
262 263
263 264 news
264 265 ++++
265 266
266 267 fixes
267 268 +++++
268 269
269 270 - #340 Celery complains about MySQL server gone away, added session cleanup
270 271 for celery tasks
271 272 - #341 "scanning for repositories in None" log message during Rescan was missing
272 273 a parameter
273 274 - fixed creating archives with subrepos. Some hooks were triggered during that
274 275 operation leading to crash.
275 276 - fixed missing email in account page.
276 277 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
277 278 forking on windows impossible
278 279
279 280 1.2.4 (**2012-01-19**)
280 281 ----------------------
281 282
282 283 news
283 284 ++++
284 285
285 286 - RhodeCode is bundled with mercurial series 2.0.X by default, with
286 287 full support to largefiles extension. Enabled by default in new installations
287 288 - #329 Ability to Add/Remove Groups to/from a Repository via AP
288 289 - added requires.txt file with requirements
289 290
290 291 fixes
291 292 +++++
292 293
293 294 - fixes db session issues with celery when emailing admins
294 295 - #331 RhodeCode mangles repository names if the a repository group
295 296 contains the "full path" to the repositories
296 297 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
297 298 - DB session cleanup after hg protocol operations, fixes issues with
298 299 `mysql has gone away` errors
299 300 - #333 doc fixes for get_repo api function
300 301 - #271 rare JSON serialization problem with statistics enabled
301 302 - #337 Fixes issues with validation of repository name conflicting with
302 303 a group name. A proper message is now displayed.
303 304 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
304 305 doesn't work
305 306 - #316 fixes issues with web description in hgrc files
306 307
307 308 1.2.3 (**2011-11-02**)
308 309 ----------------------
309 310
310 311 news
311 312 ++++
312 313
313 314 - added option to manage repos group for non admin users
314 315 - added following API methods for get_users, create_user, get_users_groups,
315 316 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
316 317 get_repo, create_repo, add_user_to_repo
317 318 - implements #237 added password confirmation for my account
318 319 and admin edit user.
319 320 - implements #291 email notification for global events are now sent to all
320 321 administrator users, and global config email.
321 322
322 323 fixes
323 324 +++++
324 325
325 326 - added option for passing auth method for smtp mailer
326 327 - #276 issue with adding a single user with id>10 to usergroups
327 328 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
328 329 - #288 fixes managing of repos in a group for non admin user
329 330
330 331 1.2.2 (**2011-10-17**)
331 332 ----------------------
332 333
333 334 news
334 335 ++++
335 336
336 337 - #226 repo groups are available by path instead of numerical id
337 338
338 339 fixes
339 340 +++++
340 341
341 342 - #259 Groups with the same name but with different parent group
342 343 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
343 344 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
344 345 - #265 ldap save fails sometimes on converting attributes to booleans,
345 346 added getter and setter into model that will prevent from this on db model level
346 347 - fixed problems with timestamps issues #251 and #213
347 348 - fixes #266 RhodeCode allows to create repo with the same name and in
348 349 the same parent as group
349 350 - fixes #245 Rescan of the repositories on Windows
350 351 - fixes #248 cannot edit repos inside a group on windows
351 352 - fixes #219 forking problems on windows
352 353
353 354 1.2.1 (**2011-10-08**)
354 355 ----------------------
355 356
356 357 news
357 358 ++++
358 359
359 360
360 361 fixes
361 362 +++++
362 363
363 364 - fixed problems with basic auth and push problems
364 365 - gui fixes
365 366 - fixed logger
366 367
367 368 1.2.0 (**2011-10-07**)
368 369 ----------------------
369 370
370 371 news
371 372 ++++
372 373
373 374 - implemented #47 repository groups
374 375 - implemented #89 Can setup google analytics code from settings menu
375 376 - implemented #91 added nicer looking archive urls with more download options
376 377 like tags, branches
377 378 - implemented #44 into file browsing, and added follow branch option
378 379 - implemented #84 downloads can be enabled/disabled for each repository
379 380 - anonymous repository can be cloned without having to pass default:default
380 381 into clone url
381 382 - fixed #90 whoosh indexer can index chooses repositories passed in command
382 383 line
383 384 - extended journal with day aggregates and paging
384 385 - implemented #107 source code lines highlight ranges
385 386 - implemented #93 customizable changelog on combined revision ranges -
386 387 equivalent of githubs compare view
387 388 - implemented #108 extended and more powerful LDAP configuration
388 389 - implemented #56 users groups
389 390 - major code rewrites optimized codes for speed and memory usage
390 391 - raw and diff downloads are now in git format
391 392 - setup command checks for write access to given path
392 393 - fixed many issues with international characters and unicode. It uses utf8
393 394 decode with replace to provide less errors even with non utf8 encoded strings
394 395 - #125 added API KEY access to feeds
395 396 - #109 Repository can be created from external Mercurial link (aka. remote
396 397 repository, and manually updated (via pull) from admin panel
397 398 - beta git support - push/pull server + basic view for git repos
398 399 - added followers page and forks page
399 400 - server side file creation (with binary file upload interface)
400 401 and edition with commits powered by codemirror
401 402 - #111 file browser file finder, quick lookup files on whole file tree
402 403 - added quick login sliding menu into main page
403 404 - changelog uses lazy loading of affected files details, in some scenarios
404 405 this can improve speed of changelog page dramatically especially for
405 406 larger repositories.
406 407 - implements #214 added support for downloading subrepos in download menu.
407 408 - Added basic API for direct operations on rhodecode via JSON
408 409 - Implemented advanced hook management
409 410
410 411 fixes
411 412 +++++
412 413
413 414 - fixed file browser bug, when switching into given form revision the url was
414 415 not changing
415 416 - fixed propagation to error controller on simplehg and simplegit middlewares
416 417 - fixed error when trying to make a download on empty repository
417 418 - fixed problem with '[' chars in commit messages in journal
418 419 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
419 420 - journal fork fixes
420 421 - removed issue with space inside renamed repository after deletion
421 422 - fixed strange issue on formencode imports
422 423 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
423 424 - #150 fixes for errors on repositories mapped in db but corrupted in
424 425 filesystem
425 426 - fixed problem with ascendant characters in realm #181
426 427 - fixed problem with sqlite file based database connection pool
427 428 - whoosh indexer and code stats share the same dynamic extensions map
428 429 - fixes #188 - relationship delete of repo_to_perm entry on user removal
429 430 - fixes issue #189 Trending source files shows "show more" when no more exist
430 431 - fixes issue #197 Relative paths for pidlocks
431 432 - fixes issue #198 password will require only 3 chars now for login form
432 433 - fixes issue #199 wrong redirection for non admin users after creating a repository
433 434 - fixes issues #202, bad db constraint made impossible to attach same group
434 435 more than one time. Affects only mysql/postgres
435 436 - fixes #218 os.kill patch for windows was missing sig param
436 437 - improved rendering of dag (they are not trimmed anymore when number of
437 438 heads exceeds 5)
438 439
439 440 1.1.8 (**2011-04-12**)
440 441 ----------------------
441 442
442 443 news
443 444 ++++
444 445
445 446 - improved windows support
446 447
447 448 fixes
448 449 +++++
449 450
450 451 - fixed #140 freeze of python dateutil library, since new version is python2.x
451 452 incompatible
452 453 - setup-app will check for write permission in given path
453 454 - cleaned up license info issue #149
454 455 - fixes for issues #137,#116 and problems with unicode and accented characters.
455 456 - fixes crashes on gravatar, when passed in email as unicode
456 457 - fixed tooltip flickering problems
457 458 - fixed came_from redirection on windows
458 459 - fixed logging modules, and sql formatters
459 460 - windows fixes for os.kill issue #133
460 461 - fixes path splitting for windows issues #148
461 462 - fixed issue #143 wrong import on migration to 1.1.X
462 463 - fixed problems with displaying binary files, thanks to Thomas Waldmann
463 464 - removed name from archive files since it's breaking ui for long repo names
464 465 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
465 466 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
466 467 Thomas Waldmann
467 468 - fixed issue #166 summary pager was skipping 10 revisions on second page
468 469
469 470
470 471 1.1.7 (**2011-03-23**)
471 472 ----------------------
472 473
473 474 news
474 475 ++++
475 476
476 477 fixes
477 478 +++++
478 479
479 480 - fixed (again) #136 installation support for FreeBSD
480 481
481 482
482 483 1.1.6 (**2011-03-21**)
483 484 ----------------------
484 485
485 486 news
486 487 ++++
487 488
488 489 fixes
489 490 +++++
490 491
491 492 - fixed #136 installation support for FreeBSD
492 493 - RhodeCode will check for python version during installation
493 494
494 495 1.1.5 (**2011-03-17**)
495 496 ----------------------
496 497
497 498 news
498 499 ++++
499 500
500 501 - basic windows support, by exchanging pybcrypt into sha256 for windows only
501 502 highly inspired by idea of mantis406
502 503
503 504 fixes
504 505 +++++
505 506
506 507 - fixed sorting by author in main page
507 508 - fixed crashes with diffs on binary files
508 509 - fixed #131 problem with boolean values for LDAP
509 510 - fixed #122 mysql problems thanks to striker69
510 511 - fixed problem with errors on calling raw/raw_files/annotate functions
511 512 with unknown revisions
512 513 - fixed returned rawfiles attachment names with international character
513 514 - cleaned out docs, big thanks to Jason Harris
514 515
515 516 1.1.4 (**2011-02-19**)
516 517 ----------------------
517 518
518 519 news
519 520 ++++
520 521
521 522 fixes
522 523 +++++
523 524
524 525 - fixed formencode import problem on settings page, that caused server crash
525 526 when that page was accessed as first after server start
526 527 - journal fixes
527 528 - fixed option to access repository just by entering http://server/<repo_name>
528 529
529 530 1.1.3 (**2011-02-16**)
530 531 ----------------------
531 532
532 533 news
533 534 ++++
534 535
535 536 - implemented #102 allowing the '.' character in username
536 537 - added option to access repository just by entering http://server/<repo_name>
537 538 - celery task ignores result for better performance
538 539
539 540 fixes
540 541 +++++
541 542
542 543 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
543 544 apollo13 and Johan Walles
544 545 - small fixes in journal
545 546 - fixed problems with getting setting for celery from .ini files
546 547 - registration, password reset and login boxes share the same title as main
547 548 application now
548 549 - fixed #113: to high permissions to fork repository
549 550 - fixed problem with '[' chars in commit messages in journal
550 551 - removed issue with space inside renamed repository after deletion
551 552 - db transaction fixes when filesystem repository creation failed
552 553 - fixed #106 relation issues on databases different than sqlite
553 554 - fixed static files paths links to use of url() method
554 555
555 556 1.1.2 (**2011-01-12**)
556 557 ----------------------
557 558
558 559 news
559 560 ++++
560 561
561 562
562 563 fixes
563 564 +++++
564 565
565 566 - fixes #98 protection against float division of percentage stats
566 567 - fixed graph bug
567 568 - forced webhelpers version since it was making troubles during installation
568 569
569 570 1.1.1 (**2011-01-06**)
570 571 ----------------------
571 572
572 573 news
573 574 ++++
574 575
575 576 - added force https option into ini files for easier https usage (no need to
576 577 set server headers with this options)
577 578 - small css updates
578 579
579 580 fixes
580 581 +++++
581 582
582 583 - fixed #96 redirect loop on files view on repositories without changesets
583 584 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
584 585 and server crashed with errors
585 586 - fixed large tooltips problems on main page
586 587 - fixed #92 whoosh indexer is more error proof
587 588
588 589 1.1.0 (**2010-12-18**)
589 590 ----------------------
590 591
591 592 news
592 593 ++++
593 594
594 595 - rewrite of internals for vcs >=0.1.10
595 596 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
596 597 with older clients
597 598 - anonymous access, authentication via ldap
598 599 - performance upgrade for cached repos list - each repository has its own
599 600 cache that's invalidated when needed.
600 601 - performance upgrades on repositories with large amount of commits (20K+)
601 602 - main page quick filter for filtering repositories
602 603 - user dashboards with ability to follow chosen repositories actions
603 604 - sends email to admin on new user registration
604 605 - added cache/statistics reset options into repository settings
605 606 - more detailed action logger (based on hooks) with pushed changesets lists
606 607 and options to disable those hooks from admin panel
607 608 - introduced new enhanced changelog for merges that shows more accurate results
608 609 - new improved and faster code stats (based on pygments lexers mapping tables,
609 610 showing up to 10 trending sources for each repository. Additionally stats
610 611 can be disabled in repository settings.
611 612 - gui optimizations, fixed application width to 1024px
612 613 - added cut off (for large files/changesets) limit into config files
613 614 - whoosh, celeryd, upgrade moved to paster command
614 615 - other than sqlite database backends can be used
615 616
616 617 fixes
617 618 +++++
618 619
619 620 - fixes #61 forked repo was showing only after cache expired
620 621 - fixes #76 no confirmation on user deletes
621 622 - fixes #66 Name field misspelled
622 623 - fixes #72 block user removal when he owns repositories
623 624 - fixes #69 added password confirmation fields
624 625 - fixes #87 RhodeCode crashes occasionally on updating repository owner
625 626 - fixes #82 broken annotations on files with more than 1 blank line at the end
626 627 - a lot of fixes and tweaks for file browser
627 628 - fixed detached session issues
628 629 - fixed when user had no repos he would see all repos listed in my account
629 630 - fixed ui() instance bug when global hgrc settings was loaded for server
630 631 instance and all hgrc options were merged with our db ui() object
631 632 - numerous small bugfixes
632 633
633 634 (special thanks for TkSoh for detailed feedback)
634 635
635 636
636 637 1.0.2 (**2010-11-12**)
637 638 ----------------------
638 639
639 640 news
640 641 ++++
641 642
642 643 - tested under python2.7
643 644 - bumped sqlalchemy and celery versions
644 645
645 646 fixes
646 647 +++++
647 648
648 649 - fixed #59 missing graph.js
649 650 - fixed repo_size crash when repository had broken symlinks
650 651 - fixed python2.5 crashes.
651 652
652 653
653 654 1.0.1 (**2010-11-10**)
654 655 ----------------------
655 656
656 657 news
657 658 ++++
658 659
659 660 - small css updated
660 661
661 662 fixes
662 663 +++++
663 664
664 665 - fixed #53 python2.5 incompatible enumerate calls
665 666 - fixed #52 disable mercurial extension for web
666 667 - fixed #51 deleting repositories don't delete it's dependent objects
667 668
668 669
669 670 1.0.0 (**2010-11-02**)
670 671 ----------------------
671 672
672 673 - security bugfix simplehg wasn't checking for permissions on commands
673 674 other than pull or push.
674 675 - fixed doubled messages after push or pull in admin journal
675 676 - templating and css corrections, fixed repo switcher on chrome, updated titles
676 677 - admin menu accessible from options menu on repository view
677 678 - permissions cached queries
678 679
679 680 1.0.0rc4 (**2010-10-12**)
680 681 --------------------------
681 682
682 683 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
683 684 - removed cache_manager settings from sqlalchemy meta
684 685 - added sqlalchemy cache settings to ini files
685 686 - validated password length and added second try of failure on paster setup-app
686 687 - fixed setup database destroy prompt even when there was no db
687 688
688 689
689 690 1.0.0rc3 (**2010-10-11**)
690 691 -------------------------
691 692
692 693 - fixed i18n during installation.
693 694
694 695 1.0.0rc2 (**2010-10-11**)
695 696 -------------------------
696 697
697 698 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
698 699 occure. After vcs is fixed it'll be put back again.
699 700 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,182 +1,183 b''
1 1 from rhodecode.lib.rcmail.response import MailResponse
2 2
3 3 from rhodecode.lib.rcmail.exceptions import BadHeaders
4 4 from rhodecode.lib.rcmail.exceptions import InvalidMessage
5 5
6
6 7 class Attachment(object):
7 8 """
8 9 Encapsulates file attachment information.
9 10
10 11 :param filename: filename of attachment
11 12 :param content_type: file mimetype
12 13 :param data: the raw file data, either as string or file obj
13 14 :param disposition: content-disposition (if any)
14 15 """
15 16
16 17 def __init__(self,
17 18 filename=None,
18 19 content_type=None,
19 20 data=None,
20 21 disposition=None):
21 22
22 23 self.filename = filename
23 24 self.content_type = content_type
24 25 self.disposition = disposition or 'attachment'
25 26 self._data = data
26 27
27 28 @property
28 29 def data(self):
29 30 if isinstance(self._data, basestring):
30 31 return self._data
31 32 self._data = self._data.read()
32 33 return self._data
33 34
34 35
35 36 class Message(object):
36 37 """
37 38 Encapsulates an email message.
38 39
39 40 :param subject: email subject header
40 41 :param recipients: list of email addresses
41 42 :param body: plain text message
42 43 :param html: HTML message
43 44 :param sender: email sender address
44 45 :param cc: CC list
45 46 :param bcc: BCC list
46 47 :param extra_headers: dict of extra email headers
47 48 :param attachments: list of Attachment instances
48 49 :param recipients_separator: alternative separator for any of
49 50 'From', 'To', 'Delivered-To', 'Cc', 'Bcc' fields
50 51 """
51 52
52 53 def __init__(self,
53 54 subject=None,
54 55 recipients=None,
55 56 body=None,
56 57 html=None,
57 58 sender=None,
58 59 cc=None,
59 60 bcc=None,
60 61 extra_headers=None,
61 62 attachments=None,
62 63 recipients_separator="; "):
63 64
64 65 self.subject = subject or ''
65 66 self.sender = sender
66 67 self.body = body
67 68 self.html = html
68 69
69 70 self.recipients = recipients or []
70 71 self.attachments = attachments or []
71 72 self.cc = cc or []
72 73 self.bcc = bcc or []
73 74 self.extra_headers = extra_headers or {}
74 75
75 76 self.recipients_separator = recipients_separator
76 77
77 78 @property
78 79 def send_to(self):
79 80 return set(self.recipients) | set(self.bcc or ()) | set(self.cc or ())
80 81
81 82 def to_message(self):
82 83 """
83 84 Returns raw email.Message instance.Validates message first.
84 85 """
85 86
86 87 self.validate()
87 88
88 89 return self.get_response().to_message()
89 90
90 91 def get_response(self):
91 92 """
92 93 Creates a Lamson MailResponse instance
93 94 """
94 95
95 96 response = MailResponse(Subject=self.subject,
96 97 To=self.recipients,
97 98 From=self.sender,
98 99 Body=self.body,
99 100 Html=self.html,
100 101 separator=self.recipients_separator)
101 102
102 103 if self.cc:
103 104 response.base['Cc'] = self.cc
104 105
105 106 for attachment in self.attachments:
106 107
107 108 response.attach(attachment.filename,
108 109 attachment.content_type,
109 110 attachment.data,
110 111 attachment.disposition)
111 112
112 113 response.update(self.extra_headers)
113 114
114 115 return response
115 116
116 117 def is_bad_headers(self):
117 118 """
118 119 Checks for bad headers i.e. newlines in subject, sender or recipients.
119 120 """
120 121
121 122 headers = [self.subject, self.sender]
122 123 headers += list(self.send_to)
123 124 headers += self.extra_headers.values()
124 125
125 126 for val in headers:
126 127 for c in '\r\n':
127 128 if c in val:
128 129 return True
129 130 return False
130 131
131 132 def validate(self):
132 133 """
133 134 Checks if message is valid and raises appropriate exception.
134 135 """
135 136
136 137 if not self.recipients:
137 raise InvalidMessage, "No recipients have been added"
138 raise InvalidMessage("No recipients have been added")
138 139
139 140 if not self.body and not self.html:
140 raise InvalidMessage, "No body has been set"
141 raise InvalidMessage("No body has been set")
141 142
142 143 if not self.sender:
143 raise InvalidMessage, "No sender address has been set"
144 raise InvalidMessage("No sender address has been set")
144 145
145 146 if self.is_bad_headers():
146 147 raise BadHeaders
147 148
148 149 def add_recipient(self, recipient):
149 150 """
150 151 Adds another recipient to the message.
151 152
152 153 :param recipient: email address of recipient.
153 154 """
154 155
155 156 self.recipients.append(recipient)
156 157
157 158 def add_cc(self, recipient):
158 159 """
159 160 Adds an email address to the CC list.
160 161
161 162 :param recipient: email address of recipient.
162 163 """
163 164
164 165 self.cc.append(recipient)
165 166
166 167 def add_bcc(self, recipient):
167 168 """
168 169 Adds an email address to the BCC list.
169 170
170 171 :param recipient: email address of recipient.
171 172 """
172 173
173 174 self.bcc.append(recipient)
174 175
175 176 def attach(self, attachment):
176 177 """
177 178 Adds an attachment to the message.
178 179
179 180 :param attachment: an **Attachment** instance.
180 181 """
181 182
182 183 self.attachments.append(attachment)
@@ -1,449 +1,453 b''
1 1 # The code in this module is entirely lifted from the Lamson project
2 2 # (http://lamsonproject.org/). Its copyright is:
3 3
4 4 # Copyright (c) 2008, Zed A. Shaw
5 5 # All rights reserved.
6 6
7 7 # It is provided under this license:
8 8
9 9 # Redistribution and use in source and binary forms, with or without
10 10 # modification, are permitted provided that the following conditions are met:
11 11
12 12 # * Redistributions of source code must retain the above copyright notice, this
13 13 # list of conditions and the following disclaimer.
14 14
15 15 # * Redistributions in binary form must reproduce the above copyright notice,
16 16 # this list of conditions and the following disclaimer in the documentation
17 17 # and/or other materials provided with the distribution.
18 18
19 19 # * Neither the name of the Zed A. Shaw nor the names of its contributors may
20 20 # be used to endorse or promote products derived from this software without
21 21 # specific prior written permission.
22 22
23 23 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 24 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 25 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 26 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 27 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28 28 # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 29 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 30 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 31 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 32 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 33 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 34 # POSSIBILITY OF SUCH DAMAGE.
35 35
36 36 import os
37 37 import mimetypes
38 38 import string
39 39 from email import encoders
40 40 from email.charset import Charset
41 41 from email.utils import parseaddr
42 42 from email.mime.base import MIMEBase
43 43
44 44 ADDRESS_HEADERS_WHITELIST = ['From', 'To', 'Delivered-To', 'Cc']
45 45 DEFAULT_ENCODING = "utf-8"
46 46 VALUE_IS_EMAIL_ADDRESS = lambda v: '@' in v
47 47
48 48
49 49 def normalize_header(header):
50 50 return string.capwords(header.lower(), '-')
51 51
52 52
53 53 class EncodingError(Exception):
54 54 """Thrown when there is an encoding error."""
55 55 pass
56 56
57 57
58 58 class MailBase(object):
59 59 """MailBase is used as the basis of lamson.mail and contains the basics of
60 60 encoding an email. You actually can do all your email processing with this
61 61 class, but it's more raw.
62 62 """
63 63 def __init__(self, items=()):
64 64 self.headers = dict(items)
65 65 self.parts = []
66 66 self.body = None
67 67 self.content_encoding = {'Content-Type': (None, {}),
68 68 'Content-Disposition': (None, {}),
69 69 'Content-Transfer-Encoding': (None, {})}
70 70
71 71 def __getitem__(self, key):
72 72 return self.headers.get(normalize_header(key), None)
73 73
74 74 def __len__(self):
75 75 return len(self.headers)
76 76
77 77 def __iter__(self):
78 78 return iter(self.headers)
79 79
80 80 def __contains__(self, key):
81 81 return normalize_header(key) in self.headers
82 82
83 83 def __setitem__(self, key, value):
84 84 self.headers[normalize_header(key)] = value
85 85
86 86 def __delitem__(self, key):
87 87 del self.headers[normalize_header(key)]
88 88
89 89 def __nonzero__(self):
90 90 return self.body != None or len(self.headers) > 0 or len(self.parts) > 0
91 91
92 92 def keys(self):
93 93 """Returns the sorted keys."""
94 94 return sorted(self.headers.keys())
95 95
96 96 def attach_file(self, filename, data, ctype, disposition):
97 97 """
98 98 A file attachment is a raw attachment with a disposition that
99 99 indicates the file name.
100 100 """
101 101 assert filename, "You can't attach a file without a filename."
102 102 ctype = ctype.lower()
103 103
104 104 part = MailBase()
105 105 part.body = data
106 106 part.content_encoding['Content-Type'] = (ctype, {'name': filename})
107 107 part.content_encoding['Content-Disposition'] = (disposition,
108 108 {'filename': filename})
109 109 self.parts.append(part)
110 110
111 111 def attach_text(self, data, ctype):
112 112 """
113 113 This attaches a simpler text encoded part, which doesn't have a
114 114 filename.
115 115 """
116 116 ctype = ctype.lower()
117 117
118 118 part = MailBase()
119 119 part.body = data
120 120 part.content_encoding['Content-Type'] = (ctype, {})
121 121 self.parts.append(part)
122 122
123 123 def walk(self):
124 124 for p in self.parts:
125 125 yield p
126 126 for x in p.walk():
127 127 yield x
128 128
129 129
130 130 class MailResponse(object):
131 131 """
132 132 You are given MailResponse objects from the lamson.view methods, and
133 133 whenever you want to generate an email to send to someone. It has the
134 134 same basic functionality as MailRequest, but it is designed to be written
135 135 to, rather than read from (although you can do both).
136 136
137 137 You can easily set a Body or Html during creation or after by passing it
138 138 as __init__ parameters, or by setting those attributes.
139 139
140 140 You can initially set the From, To, and Subject, but they are headers so
141 141 use the dict notation to change them: msg['From'] = 'joe@test.com'.
142 142
143 143 The message is not fully crafted until right when you convert it with
144 144 MailResponse.to_message. This lets you change it and work with it, then
145 145 send it out when it's ready.
146 146 """
147 147 def __init__(self, To=None, From=None, Subject=None, Body=None, Html=None,
148 148 separator="; "):
149 149 self.Body = Body
150 150 self.Html = Html
151 151 self.base = MailBase([('To', To), ('From', From), ('Subject', Subject)])
152 152 self.multipart = self.Body and self.Html
153 153 self.attachments = []
154 154 self.separator = separator
155 155
156 156 def __contains__(self, key):
157 157 return self.base.__contains__(key)
158 158
159 159 def __getitem__(self, key):
160 160 return self.base.__getitem__(key)
161 161
162 162 def __setitem__(self, key, val):
163 163 return self.base.__setitem__(key, val)
164 164
165 165 def __delitem__(self, name):
166 166 del self.base[name]
167 167
168 168 def attach(self, filename=None, content_type=None, data=None,
169 169 disposition=None):
170 170 """
171 171
172 172 Simplifies attaching files from disk or data as files. To attach
173 173 simple text simple give data and a content_type. To attach a file,
174 174 give the data/content_type/filename/disposition combination.
175 175
176 176 For convenience, if you don't give data and only a filename, then it
177 177 will read that file's contents when you call to_message() later. If
178 178 you give data and filename then it will assume you've filled data
179 179 with what the file's contents are and filename is just the name to
180 180 use.
181 181 """
182 182
183 183 assert filename or data, ("You must give a filename or some data to "
184 184 "attach.")
185 185 assert data or os.path.exists(filename), ("File doesn't exist, and no "
186 186 "data given.")
187 187
188 188 self.multipart = True
189 189
190 190 if filename and not content_type:
191 191 content_type, encoding = mimetypes.guess_type(filename)
192 192
193 193 assert content_type, ("No content type given, and couldn't guess "
194 194 "from the filename: %r" % filename)
195 195
196 196 self.attachments.append({'filename': filename,
197 197 'content_type': content_type,
198 198 'data': data,
199 199 'disposition': disposition,})
200 200
201 201 def attach_part(self, part):
202 202 """
203 203 Attaches a raw MailBase part from a MailRequest (or anywhere)
204 204 so that you can copy it over.
205 205 """
206 206 self.multipart = True
207 207
208 208 self.attachments.append({'filename': None,
209 209 'content_type': None,
210 210 'data': None,
211 211 'disposition': None,
212 212 'part': part,
213 213 })
214 214
215 215 def attach_all_parts(self, mail_request):
216 216 """
217 217 Used for copying the attachment parts of a mail.MailRequest
218 218 object for mailing lists that need to maintain attachments.
219 219 """
220 220 for part in mail_request.all_parts():
221 221 self.attach_part(part)
222 222
223 223 self.base.content_encoding = mail_request.base.content_encoding.copy()
224 224
225 225 def clear(self):
226 226 """
227 227 Clears out the attachments so you can redo them. Use this to keep the
228 228 headers for a series of different messages with different attachments.
229 229 """
230 230 del self.attachments[:]
231 231 del self.base.parts[:]
232 232 self.multipart = False
233 233
234 234 def update(self, message):
235 235 """
236 236 Used to easily set a bunch of heading from another dict
237 237 like object.
238 238 """
239 239 for k in message.keys():
240 240 self.base[k] = message[k]
241 241
242 242 def __str__(self):
243 243 """
244 244 Converts to a string.
245 245 """
246 246 return self.to_message().as_string()
247 247
248 248 def _encode_attachment(self, filename=None, content_type=None, data=None,
249 249 disposition=None, part=None):
250 250 """
251 251 Used internally to take the attachments mentioned in self.attachments
252 252 and do the actual encoding in a lazy way when you call to_message.
253 253 """
254 254 if part:
255 255 self.base.parts.append(part)
256 256 elif filename:
257 257 if not data:
258 258 data = open(filename).read()
259 259
260 260 self.base.attach_file(filename, data, content_type,
261 261 disposition or 'attachment')
262 262 else:
263 263 self.base.attach_text(data, content_type)
264 264
265 265 ctype = self.base.content_encoding['Content-Type'][0]
266 266
267 267 if ctype and not ctype.startswith('multipart'):
268 268 self.base.content_encoding['Content-Type'] = ('multipart/mixed', {})
269 269
270 270 def to_message(self):
271 271 """
272 272 Figures out all the required steps to finally craft the
273 273 message you need and return it. The resulting message
274 274 is also available as a self.base attribute.
275 275
276 276 What is returned is a Python email API message you can
277 277 use with those APIs. The self.base attribute is the raw
278 278 lamson.encoding.MailBase.
279 279 """
280 280 del self.base.parts[:]
281 281
282 282 if self.Body and self.Html:
283 283 self.multipart = True
284 284 self.base.content_encoding['Content-Type'] = (
285 285 'multipart/alternative', {})
286 286
287 287 if self.multipart:
288 288 self.base.body = None
289 289 if self.Body:
290 290 self.base.attach_text(self.Body, 'text/plain')
291 291
292 292 if self.Html:
293 293 self.base.attach_text(self.Html, 'text/html')
294 294
295 295 for args in self.attachments:
296 296 self._encode_attachment(**args)
297 297
298 298 elif self.Body:
299 299 self.base.body = self.Body
300 300 self.base.content_encoding['Content-Type'] = ('text/plain', {})
301 301
302 302 elif self.Html:
303 303 self.base.body = self.Html
304 304 self.base.content_encoding['Content-Type'] = ('text/html', {})
305 305
306 306 return to_message(self.base, separator=self.separator)
307 307
308 308 def all_parts(self):
309 309 """
310 310 Returns all the encoded parts. Only useful for debugging
311 311 or inspecting after calling to_message().
312 312 """
313 313 return self.base.parts
314 314
315 315 def keys(self):
316 316 return self.base.keys()
317 317
318 318
319 319 def to_message(mail, separator="; "):
320 320 """
321 321 Given a MailBase message, this will construct a MIMEPart
322 322 that is canonicalized for use with the Python email API.
323 323 """
324 324 ctype, params = mail.content_encoding['Content-Type']
325 325
326 326 if not ctype:
327 327 if mail.parts:
328 328 ctype = 'multipart/mixed'
329 329 else:
330 330 ctype = 'text/plain'
331 331 else:
332 332 if mail.parts:
333 333 assert ctype.startswith(("multipart", "message")), \
334 334 "Content type should be multipart or message, not %r" % ctype
335 335
336 336 # adjust the content type according to what it should be now
337 337 mail.content_encoding['Content-Type'] = (ctype, params)
338 338
339 339 try:
340 340 out = MIMEPart(ctype, **params)
341 341 except TypeError, exc: # pragma: no cover
342 342 raise EncodingError("Content-Type malformed, not allowed: %r; "
343 343 "%r (Python ERROR: %s" %
344 344 (ctype, params, exc.message))
345 345
346 346 for k in mail.keys():
347 347 if k in ADDRESS_HEADERS_WHITELIST:
348 348 out[k.encode('ascii')] = header_to_mime_encoding(
349 349 mail[k],
350 350 not_email=False,
351 351 separator=separator
352 352 )
353 353 else:
354 354 out[k.encode('ascii')] = header_to_mime_encoding(
355 355 mail[k],
356 356 not_email=True
357 357 )
358 358
359 359 out.extract_payload(mail)
360 360
361 361 # go through the children
362 362 for part in mail.parts:
363 363 out.attach(to_message(part))
364 364
365 365 return out
366 366
367
367 368 class MIMEPart(MIMEBase):
368 369 """
369 370 A reimplementation of nearly everything in email.mime to be more useful
370 371 for actually attaching things. Rather than one class for every type of
371 372 thing you'd encode, there's just this one, and it figures out how to
372 373 encode what you ask it.
373 374 """
374 375 def __init__(self, type, **params):
375 376 self.maintype, self.subtype = type.split('/')
376 377 MIMEBase.__init__(self, self.maintype, self.subtype, **params)
377 378
378 379 def add_text(self, content):
379 380 # this is text, so encode it in canonical form
380 381 try:
381 382 encoded = content.encode('ascii')
382 383 charset = 'ascii'
383 384 except UnicodeError:
384 385 encoded = content.encode('utf-8')
385 386 charset = 'utf-8'
386 387
387 388 self.set_payload(encoded, charset=charset)
388 389
389 390 def extract_payload(self, mail):
390 if mail.body == None: return # only None, '' is still ok
391 if mail.body == None:
392 return # only None, '' is still ok
391 393
392 394 ctype, ctype_params = mail.content_encoding['Content-Type']
393 395 cdisp, cdisp_params = mail.content_encoding['Content-Disposition']
394 396
395 397 assert ctype, ("Extract payload requires that mail.content_encoding "
396 398 "have a valid Content-Type.")
397 399
398 400 if ctype.startswith("text/"):
399 401 self.add_text(mail.body)
400 402 else:
401 403 if cdisp:
402 404 # replicate the content-disposition settings
403 405 self.add_header('Content-Disposition', cdisp, **cdisp_params)
404 406
405 407 self.set_payload(mail.body)
406 408 encoders.encode_base64(self)
407 409
408 410 def __repr__(self):
409 411 return "<MIMEPart '%s/%s': %r, %r, multipart=%r>" % (
410 412 self.subtype,
411 413 self.maintype,
412 414 self['Content-Type'],
413 415 self['Content-Disposition'],
414 416 self.is_multipart())
415 417
416 418
417 419 def header_to_mime_encoding(value, not_email=False, separator=", "):
418 if not value: return ""
420 if not value:
421 return ""
419 422
420 423 encoder = Charset(DEFAULT_ENCODING)
421 424 if type(value) == list:
422 425 return separator.join(properly_encode_header(
423 426 v, encoder, not_email) for v in value)
424 427 else:
425 428 return properly_encode_header(value, encoder, not_email)
426 429
430
427 431 def properly_encode_header(value, encoder, not_email):
428 432 """
429 433 The only thing special (weird) about this function is that it tries
430 434 to do a fast check to see if the header value has an email address in
431 435 it. Since random headers could have an email address, and email addresses
432 436 have weird special formatting rules, we have to check for it.
433 437
434 438 Normally this works fine, but in Librelist, we need to "obfuscate" email
435 439 addresses by changing the '@' to '-AT-'. This is where
436 440 VALUE_IS_EMAIL_ADDRESS exists. It's a simple lambda returning True/False
437 441 to check if a header value has an email address. If you need to make this
438 442 check different, then change this.
439 443 """
440 444 try:
441 445 return value.encode("ascii")
442 446 except UnicodeEncodeError:
443 447 if not_email is False and VALUE_IS_EMAIL_ADDRESS(value):
444 448 # this could have an email address, make sure we don't screw it up
445 449 name, address = parseaddr(value)
446 450 return '"%s" <%s>' % (
447 451 encoder.header_encode(name.encode("utf-8")), address)
448 452
449 453 return encoder.header_encode(value.encode("utf-8"))
@@ -1,94 +1,98 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.rcmail.smtp_mailer
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Simple smtp mailer used in RhodeCode
7 7
8 8 :created_on: Sep 13, 2010
9 9 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 10 :license: GPLv3, see COPYING for more details.
11 11 """
12 12 # This program is free software: you can redistribute it and/or modify
13 13 # it under the terms of the GNU General Public License as published by
14 14 # the Free Software Foundation, either version 3 of the License, or
15 15 # (at your option) any later version.
16 16 #
17 17 # This program is distributed in the hope that it will be useful,
18 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 20 # GNU General Public License for more details.
21 21 #
22 22 # You should have received a copy of the GNU General Public License
23 23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
24 import time
25 25 import logging
26 26 import smtplib
27 27 from socket import sslerror
28 from email.utils import formatdate
28 29 from rhodecode.lib.rcmail.message import Message
29 30
30 31
31 32 class SmtpMailer(object):
32 33 """SMTP mailer class
33 34
34 35 mailer = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth
35 36 mail_port, ssl, tls)
36 37 mailer.send(recipients, subject, body, attachment_files)
37 38
38 39 :param recipients might be a list of string or single string
39 40 :param attachment_files is a dict of {filename:location}
40 41 it tries to guess the mimetype and attach the file
41 42
42 43 """
43 44
44 45 def __init__(self, mail_from, user, passwd, mail_server, smtp_auth=None,
45 46 mail_port=None, ssl=False, tls=False, debug=False):
46 47
47 48 self.mail_from = mail_from
48 49 self.mail_server = mail_server
49 50 self.mail_port = mail_port
50 51 self.user = user
51 52 self.passwd = passwd
52 53 self.ssl = ssl
53 54 self.tls = tls
54 55 self.debug = debug
55 56 self.auth = smtp_auth
56 57
57 58 def send(self, recipients=[], subject='', body='', html='',
58 59 attachment_files=None):
59 60
60 61 if isinstance(recipients, basestring):
61 62 recipients = [recipients]
63 headers = {
64 'Date': formatdate(time.time())
65 }
62 66 msg = Message(subject, recipients, body, html, self.mail_from,
63 recipients_separator=", ")
67 recipients_separator=", ", extra_headers=headers)
64 68 raw_msg = msg.to_message()
65 69
66 70 if self.ssl:
67 71 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
68 72 else:
69 73 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
70 74
71 75 if self.tls:
72 76 smtp_serv.ehlo()
73 77 smtp_serv.starttls()
74 78
75 79 if self.debug:
76 80 smtp_serv.set_debuglevel(1)
77 81
78 82 smtp_serv.ehlo()
79 83 if self.auth:
80 84 smtp_serv.esmtp_features["auth"] = self.auth
81 85
82 86 # if server requires authorization you must provide login and password
83 87 # but only if we have them
84 88 if self.user and self.passwd:
85 89 smtp_serv.login(self.user, self.passwd)
86 90
87 91 smtp_serv.sendmail(msg.sender, msg.send_to, raw_msg.as_string())
88 92 logging.info('MAIL SEND TO: %s' % recipients)
89 93
90 94 try:
91 95 smtp_serv.quit()
92 96 except sslerror:
93 97 # sslerror is raised in tls connections on closing sometimes
94 98 pass
General Comments 0
You need to be logged in to leave comments. Login now