From: Tim A. <tna...@sh...> - 2011-05-10 03:04:38
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="Content-Type"> </head> <body bgcolor="#ffffff" text="#000000"> After a length discussion about inheritance issues when a user is allowed to created albums, Bharat and I have decide to postpone this fix to a later release.<br> original ticket: <a class="moz-txt-link-freetext" href="https://sourceforge.net/apps/trac/gallery/ticket/1273">https://sourceforge.net/apps/trac/gallery/ticket/1273</a><br> <br> it should probably be considered when ticket <a href="https://sourceforge.net/apps/trac/gallery/ticket/1346">https://sourceforge.net/apps/trac/gallery/ticket/1346</a> is considered<br> <br> On 5/1/2011 7:26 PM, Tim Almdal wrote: <blockquote cite="mid:4DB...@sh..." type="cite"> <meta content="text/html; charset=ISO-8859-1" http-equiv="Content-Type"> Documenting is good.<br> <br> But I'm confused again... line 115 was in the original code (if its an album use its id, if its not an album then get its parent id) isn't doing the same thing as <font face="'courier new', monospace">access::can("edit_album", X) || /* X != album? it comes from parent */</font><br> <br> Tim<br> <br> On 5/1/2011 7:09 PM, Bharat Mediratta wrote: <blockquote cite="mid:BANLkTi=U5-...@ma..." type="cite"><br> <br> <div class="gmail_quote">On Sun, May 1, 2011 at 6:31 PM, Tim Almdal <span dir="ltr"><<a moz-do-not-send="true" href="mailto:tna...@sh...">tna...@sh...</a>></span> wrote:<br> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> I'll give your suggestions a try. <br> <div class="im"> <br> I notice that you're not copying the edit permissions in the upgrader. Just to verify: we decide that was ok because "edit" is a superset of "edit_xxx" right? <br> <br> </div> <blockquote>No, it was my understanding that we didn't copy edit permissions because I thought we had finally distilled everything down to the bottom half of this <a moz-do-not-send="true" href="http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0162.JPG.html" target="_blank">http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0162.JPG.html</a>. and that we felt that edit would work just as it did now. If the administrator wanted to use the new permissions that they would have to explicitly chose to enable them.<br> </blockquote> <div class="im"> is your wrapper around _get_all_groups() really necessary? <br> </div> <blockquote>using the helper _get_all_groups() was just from copying code... your right i should be able to just call identity::groups.<br> </blockquote> <div class="im"> <br> How about we restrict organize to users who have the "edit" permission? That should be unambiguous.<b><br> </b> </div> <blockquote>works for me<br> </blockquote> <div class="im"> <br> For the remainder of this mail I'm going to assume that we rename edit->edit_album and use <i>edit</i> as an alias. <br> </div> <blockquote>Not a safe assumption, at this point, as mentioned above, I was not coding to (<a moz-do-not-send="true" href="http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0161.JPG.html" target="_blank">http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0161.JPG.html</a>) but to <a moz-do-not-send="true" href="http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0162.JPG.html" target="_blank">http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0162.JPG.html</a>. As you can see from that image, we had crossed out the rename of edit. What's in a name, so it doesn't matter to me whether we rename it or not.<br> </blockquote> </div> </blockquote> <div><br> </div> <div>Those two images are the same, except that the name is different. But the name <b>is</b> relevant because you're overloading "edit" to be a function of all three separate permissions. In your model, we have 3 permissions (edit, edit_all_photos, edit_my_photos) but access::can("edit") doesn't actually refer to the edit permission -- it refers to a function of all three. That is confusing. See my last email, starting at "I initially had a negative reaction..."</div> <div> </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> <blockquote> <div style="text-align: left;">I think that trying to extend or convert "edit/edit album" permission on albums that are added by users is a fools errand and destined to make things complicated. (We might have to </div> </blockquote> </div> </blockquote> <div><br> </div> <div>I am not suggesting that. I am merely suggesting that you rename the permissions to be internally consistent with their names. We are considering the "edit" permission to have the effect of "edit this album" and we should name it internally accordingly. We should not let access::can("edit") be ambiguous in its meaning.</div> <div> </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> <blockquote> <div style="text-align: left;">mess around with the inheritance portion of our existing permission code). I think the way to do this is force users to have one of ("edit/edit album", edit_all, or edit_mine) permissions in addition to the add_album or add_photo permissions. That way our existing inheritancemodel works naturally. I really don't like the idea (which I think you are suggesting) of when an album is added we then create ("edit/edit album") permission for the album. That's a lot of work and breaks the natural inheritance that we have now.</div> </blockquote> </div> </blockquote> <div><br> </div> <div>Forcing the permissions to relate to each other is non-trivial, and ultimately not necessary. <b>edit_album</b> refers to the album as a container and you are granted all the rights you typically get on a container (which extends to the contents of the container). <b>edit_mine</b> and <b>edit_all</b> give you rights on members of the container, but not rights on the container itself. I don't believe that we have to mess with the permission inheritance model. </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> <blockquote> <div style="text-align: left;"><br> </div> <div style="text-align: left;">What's currently in the code, allows inheritance of the "add_photo" and "add_album" permissions without regard to the edit permissions. For example you could create an album and I could add sub-albums and photo's to your album even if I can't edit them because the add_* permissions where inherited w/o regard to what my edit permissions are. Tying them together with the add_* permissions a sub-permission of the edit* permissions prevents that blank inheritance.</div> </blockquote> </div> </blockquote> <div><br> </div> <div>Sorry, I don't follow what you're saying. Add and edit permissions are separate in your model, and are separate in my proposed model. What you say above implies that I'm suggesting that we tie them together somehow, but I'm not suggesting that at all. I am merely suggesting that we consider <i>edit</i> to be an alias for a function of {edit_album, edit_mine, edit_all}.</div> <div><br> </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> <blockquote> </blockquote> <div class="im"> <div style="text-align: left;"><br> </div> <div style="text-align: left;"><font face="arial, helvetica, sans-serif">This makes our logic easier because we stop differentiating child albums vs. photos. To close the loop, I'm going to write it in pseudocode below so that I'm sure I have it right.</font></div> <div style="text-align: left;"><font face="'courier new', monospace"><br> </font></div> <div style="text-align: left;"><font face="'courier new', monospace">access::can("edit", X) ==</font></div> <div style="text-align: left;"><font face="'courier new', monospace">C1 access::can("edit_album", X) || /* X != album? it comes from parent */</font></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;">C2 access::can("edit_all", X->parent) || </span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> (</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;">C3 access::can("edit_mine", X->parent) &&</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> is_owner(X)</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> )<br> </span></div> </div> <blockquote style="text-align: left;">That's what I believe I have implemented.<br> </blockquote> </div> </blockquote> <div>No, what you have is more complicated because it's differentiating between albums and photos, when in reality according to the pseudocode I have above, that distinction is no longer necessary. See:</div> <div><a moz-do-not-send="true" href="https://github.com/gallery/gallery3/blob/1273/modules/gallery/helpers/access.php#L115">https://github.com/gallery/gallery3/blob/1273/modules/gallery/helpers/access.php#L115</a></div> <div><br> </div> <div>On that line you're checking to see if it's an album and if so, you're taking the permission of the parent. I'm saying that you don't need to do that because the parent's edit_album will apply to the child if and only if the child has not overridden it. In my model, if the child overrides edit_album, then the parent can't edit it. Hmm. Now that I think about it, this has to be wrong because it would allow the owner of a sub-album to remove the parent's ability to edit it, even though the parent has edit_album.</div> <div><br> </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> <div style="text-align: left;">So in my mind,</div> <div style="text-align: left;"> 1) Decide which diagram we are using (I'm assuming 162)</div> <div style="text-align: left;">2) Clean up the installer code especially the _copy_permission code as you suggested</div> <div style="text-align: left;">3) Change the add_* permissions to also require an edit permission (bury that check in access.php so no code has to change external to access.php to enforce that)</div> <div style="text-align: left;">4) Write some unit tests.</div> <div style="text-align: left;">5) Generate the install SQL</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">And we should be good to go</div> </div> </blockquote> <div><br> </div> <div>I don't think that we've converged yet on the right model. I think that perhaps you misunderstood what I was saying above about permission inheritance.</div> <div><br> </div> <div>I want to get all the scenarios in one place, and email isn't great for that. I'm going to start a doc which outlines all the various scenarios so that we can refer to them and then make sure that we've covered everything. I'll ping back when that's done..</div> <div><br> </div> <div>-Bharat</div> <div><br> </div> <div> </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div bgcolor="#ffffff" text="#000000"> <div style="text-align: left;"> Tim</div> <div> <div class="h5"> <div style="text-align: left;"><br> </div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">On 5/1/2011 12:54 PM, Bharat Mediratta wrote:</div> </div> </div> <blockquote type="cite"> <div> <div class="h5"> <div style="text-align: left;">(broadening the audience to include gallery-devel -- this is a continuation of a discussion that Tim and I are having around the branch to fix ticket #1273).</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">----</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">Tim,</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">I notice that you're not copying the edit permissions in the upgrader. Just to verify: we decide that was ok because "edit" is a superset of "edit_xxx" right? In this diagram:</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;"><a moz-do-not-send="true" href="http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0161.JPG.html" target="_blank">http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0161.JPG.html</a></div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">IIRC, "edit_album" is the equivalent of what you have as "edit" right now.. I wonder if we should actually rename the permission to be "Edit album" to avoid confusion there.</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">_copy_permissions is a PHP based O(N*M) operation where N==the number of rows and M== the number of groups. That's going to be really inefficient. Let's get that down to O(M) using the db builder. It'd be something like:</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">foreach ($groups as $group) {</div> <div style="text-align: left;"> ...</div> <div style="text-align: left;"> db::build()</div> <div style="text-align: left;"> ->from($table)</div> <div style="text-align: left;"> ->set($to_column, db::expr($from_column))</div> <div style="text-align: left;"> ->execute();</div> <div style="text-align: left;"> }</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">is your wrapper around _get_all_groups() really necessary? You're only calling that from the upgrader and when we get to that point there had better be an identity provider active!</div> <div style="text-align: left;"> <br> </div> <div style="text-align: left;">More below.</div> <div style="text-align: left;"><br> </div> <div class="gmail_quote"> <div style="text-align: left;">On Sat, Apr 30, 2011 at 6:11 PM, Tim Almdal <span dir="ltr"><<a moz-do-not-send="true" href="mailto:tna...@sh..." target="_blank">tna...@sh...</a>></span> wrote:</div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div style="text-align: left;">Just a little bit more information, I went and used user1 to add a photo into user2's album (the waterfall). So now there is a picture that user2 can't do anything about.</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">The other interesting thing is that user2 can now get the organize dialog and edit permission dialogs for their directory. Not sure of this is what we want.</div> </blockquote> <div style="text-align: left;"><br> </div> <div style="text-align: left;">How about we restrict organize to users who have the "edit" permission? That should be unambiguous.</div> <div style="text-align: left;"> </div> <blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <div style="text-align: left;"><br> </div> <div style="text-align: left;">My basic approach was rather than put all sorts of stuff all over the place to check edit permissions, I just changed access.php so that the interface accepts access::can("edit"...) and it figures out whether the user has edit, edit_all_photos or edit_my_photos.</div> </blockquote> <div style="text-align: left;"><br> </div> <div style="text-align: left;">I initially had a negative reaction to this idea, but I thought it through and it makes sense. There's an inconsistency that bothers me, though. Right now you have 3 perms "edit", "edit_all_photos" and "edit_my_photos". If you do access::can("edit") it's not obvious to the caller what's going on there because there <b>is</b> an edit permission. If you rename "edit" to "edit_album" (which is consistent) then "edit" doesn't directly map to a column and we can consider it an <i>alias</i> permission. We had those in G2, but they were heavyweight (using bit vectors, etc). This will just be a simple alias that you cover in access::can.</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">So then we can try to map this to the diagram we came up with:</div> <div style="text-align: left;"><a moz-do-not-send="true" href="http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0161.JPG.html" target="_blank">http://gallery.menalto.com/gallery/doc-images/gcon2011/IMG_0161.JPG.html</a></div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">For the remainder of this mail I'm going to assume that we rename edit->edit_album and use <i>edit</i> as an alias.</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">Going through the diagram, I'm convinced that your code is right for the root album (A1) and for the photos (P1, P2). However, I'm not sure that we have A2 correct. Right now your equality for A2 is:</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;"><font face="'courier new', monospace">edit(A2) == access::can("edit_album", A2) ||</font></div> <div style="text-align: left;"><font face="'courier new', monospace"> ( </font></div> <div style="text-align: left;"><font face="'courier new', monospace"> a</font><span style="font-family: 'courier new',monospace;">ccess::can("edit_all_photos", A1) ||</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> (</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> is_owner(A2) &&</span></div> <div style="text-align: left;"><font face="'courier new', monospace"> a</font><span style="font-family: 'courier new',monospace;">ccess::can("edit_my_photos", A1)</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> )</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> )</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"><br> </span></div> <div style="text-align: left;"><font face="arial, helvetica, sans-serif">I'm not sure if that's right.. If I grant a user the ability to create A2 under A1, I would imagine that they would start off with the edit_album permission -- but I could see that they wouldn't get that permission because they don't have edit_album on the parent so they wouldn't inherit it. But we still want them to be able to edit the album they just created, right? I guess that means that we really do need to treat sub-albums the same as sub-photos in that case.</font></div> <div style="text-align: left;"><font face="arial, helvetica, sans-serif"><br> </font></div> <div style="text-align: left;"><font face="arial, helvetica, sans-serif">Is that logic correct? If so, then we should rename:</font></div> <div> <ul> <li style="text-align: left;"><span style="font-family: arial,helvetica,sans-serif;">edit --> edit_album</span></li> <li style="text-align: left;"><span style="font-family: arial,helvetica,sans-serif;">edit_all_photos --> edit_all</span></li> <li style="text-align: left;"><span style="font-family: arial,helvetica,sans-serif;">edit_my_photos -> edit_mine</span></li> </ul> <div style="text-align: left;"><font face="arial, helvetica, sans-serif">This makes our logic easier because we stop differentiating child albums vs. photos. To close the loop, I'm going to write it in pseudocode below so that I'm sure I have it right.</font></div> </div> <div style="text-align: left;"><font face="'courier new', monospace"><br> </font></div> <div style="text-align: left;"><font face="'courier new', monospace">access::can("edit", X) ==</font></div> <div style="text-align: left;"><font face="'courier new', monospace">C1 access::can("edit_album", X) || /* X != album? it comes from parent */</font></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;">C2 access::can("edit_all", X->parent) || </span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> (</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;">C3 access::can("edit_mine", X->parent) &&</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> is_owner(X)</span></div> <div style="text-align: left;"><span style="font-family: 'courier new',monospace;"> )</span></div> <div style="text-align: left;"><font face="'courier new', monospace"><br> </font></div> <div style="text-align: left;"><font face="arial, helvetica, sans-serif">So that means the equalities are:</font></div> <div style="text-align: left;"><font face="arial, helvetica, sans-serif"><br> </font></div> <div style="text-align: left;"><font face="'courier new', monospace">edit(A2) == edit_album(A1) || edit_all(A1) || (edit_mine(A1) && is_owner(A2))</font></div> <div> <div style="text-align: left;"><font face="'courier new', monospace">edit(P2) == edit_album(A1) || edit_all(A1) || (edit_mine(A1) && is_owner(P2))</font></div> </div> <div style="text-align: left;"><font face="'courier new', monospace"><br> </font></div> <div style="text-align: left;">Scenarios:</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;"><b>S1: album creation:</b></div> <div> <ol> <li style="text-align: left;">Admin user grants only "view" and "add_album" to groups G1 (containing U1), G2 (U2) at the root album (A0)</li> <li style="text-align: left;">U1 creates album A1, which inherits permissions from root album. A1's permissions are now "view" and "add_album" for G1/G2. U1 <b>cannot</b> edit A1 even though they own it.</li> <li style="text-align: left;">Admin grants "edit_mine" to A0</li> <li style="text-align: left;">Now U1 can edit A1 because of permission clause C3 above.</li> <li style="text-align: left;">U2 creates album A2 and can immediately edit A2 because of step 3 and C3.</li> </ol> <div style="text-align: left;"><b>S2: photo addition (continues from scenario S1):</b></div> </div> <div> <ol> <li style="text-align: left;">U1 creates photo P1A inside A1.</li> <li style="text-align: left;">U1 can edit P1A because of C1</li> <li style="text-align: left;">U2 cannot add photos to A1</li> <li style="text-align: left;">U1 grants "add_photo" to G2 on A1</li> <li style="text-align: left;">U2 now adds photo P1B to A2</li> <li style="text-align: left;">U2 cannot edit P1B because he has no edit permissions</li> <li style="text-align: left;">U1 grants "edit_mine" to G2 on A1</li> <li style="text-align: left;">U2 can now edit P1B but not P1A <b>(** we're not talking about delete yet)</b></li> <li style="text-align: left;">U1 revokes "edit_mine" from G2 on A1</li> <li style="text-align: left;">U2 can no longer edit P1B</li> <li style="text-align: left;">U1 grants "edit_all" to G2 on A1</li> <li style="text-align: left;">U2 can now edit P1A (which he does not own) and P1B (which he owns)</li> </ol> <div style="text-align: left;"><br> </div> </div> <div style="text-align: left;">Ok. I think I'm satisfied with that. One thing I noticed is that you did not implement the aliasing for group_can(). I don't think that we need to do that currently, but right now because you haven't changed the name of the "edit" permission anybody who calls group_can("edit") is going to get an unexpected result -- they're essentially getting group_can("edit_album"), not the alias. Once you rename the permission, it means that they'll get an error, which I think is a good thing since it'll force them to figure out what to do. A quick grep around gallery3 and gallery3-contrib indicates that nobody does this outside of the core code now, so this is a good time to do this.</div> <div style="text-align: left;"><br> </div> <div style="text-align: left;">So to sum up, here's what I think we need to do:</div> <div> <ol> <li style="text-align: left;">Rename edit -> edit_album</li> <li style="text-align: left;">Rename edit_all_photos -> edit_all</li> <li style="text-align: left;">Rename edit_my_photos -> edit_mine</li> <li style="text-align: left;"> Replace the _user_can_edit() logic with what I have above, and (I suggest) you inline it into user_can()</li> <li style="text-align: left;">Write up all the above scenarios as unit tests (<b>!!!</b>)</li> </ol> <div style="text-align: left;"><br> </div> </div> <div style="text-align: left;">Whew!</div> </div> <div style="text-align: left;"><br> </div> </div> </div> <hr style="text-align: left;"> <hr style="text-align: left;" noshade="noshade" size="1"> <p color="#000000" align="left">No virus found in this message.<br> Checked by AVG - <a moz-do-not-send="true" href="http://www.avg.com" target="_blank">www.avg.com</a><br> Version: 10.0.1209 / Virus Database: 1500/3608 - Release Date: 05/01/11</p> </blockquote> </div> </blockquote> </div> <div style="text-align: left;"><br> </div> <br> <hr> <hr noshade="noshade" size="1"> <p class="avgcert" color="#000000" align="left">No virus found in this message.<br> Checked by AVG - <a moz-do-not-send="true" href="http://www.avg.com">www.avg.com</a><br> Version: 10.0.1209 / Virus Database: 1500/3609 - Release Date: 05/01/11</p> </blockquote> <pre wrap=""> <fieldset class="mimeAttachmentHeader"></fieldset> ------------------------------------------------------------------------------ WhatsUp Gold - Download Free Network Management Software The most intuitive, comprehensive, and cost-effective network management toolset available today. Delivers lowest initial acquisition cost and overall TCO of any competing solution. <a class="moz-txt-link-freetext" href="http://p.sf.net/sfu/whatsupgold-sd">http://p.sf.net/sfu/whatsupgold-sd</a></pre> <pre wrap=""> <fieldset class="mimeAttachmentHeader"></fieldset> __[ g a l l e r y - d e v e l ]_________________________ [ list info/archive --> <a class="moz-txt-link-freetext" href="http://gallery.sf.net/lists.php">http://gallery.sf.net/lists.php</a> ] [ gallery info/FAQ/download --> <a class="moz-txt-link-freetext" href="http://gallery.sf.net">http://gallery.sf.net</a> ]</pre> <br> <fieldset class="mimeAttachmentHeader"></fieldset> <br> <p class="avgcert" color="#000000" align="left">No virus found in this message.<br> Checked by AVG - <a moz-do-not-send="true" href="http://www.avg.com">www.avg.com</a><br> Version: 10.0.1209 / Virus Database: 1500/3609 - Release Date: 05/01/11</p> </blockquote> </body> </html> |