gtkcookie-0.4/0000755000175000017500000000000010117103374014743 5ustar mechanixmechanix00000000000000gtkcookie-0.4/ACKNOWLEDGEMENTS0000644000175000017500000000121606624707100017225 0ustar mechanixmechanix00000000000000Thanks to: 1. The creators of GTK+. What a rockin' toolkit! 2. Ian Main and Tony Gale, authors of the GTK tutorial (http://www.gtk.org/tutorial/). It made learning GTK+ possible. My source code will doubtless look an awful lot like their sample code. 3. Michael K. Johnson and Erik W. Troan, authors of _Linux Application Development_, a very useful book, whose influences will probably be seen in my code. 4. The authors of the GPL. 5. Linus Torvalds, Richard Stallman, Eric Raymond, and other greats of the open source software universe. 6. Martin Schulze, for making an improved Makefile for me, and for turning gtkcookie into a debian package. gtkcookie-0.4/COPYING0000644000175000017500000004310506624707100016006 0ustar mechanixmechanix00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. gtkcookie-0.4/INSTALL0000644000175000017500000000256506624707100016011 0ustar mechanixmechanix00000000000000 To build on Linux: ------------------ 1. make sure you've got the gtk+ libraries installed on your system 2. edit Makefile, if you choose. The defaults will work for most people. 2. type 'make' (without the quotes). 3. type 'make install' to install the binary, cookie icons, and man pages on your system. If you use the Makefile's defaults, everything will go in /usr/local/bin, /usr/local/man, etc. That's it! To build on other OSes: ----------------------- I'm still learning the intricacies of make and autoconf, wonderful tools that they are. gtkcookie would be quite portable, except for one call to strptime() in sys/time.h, which is not an ANSI-standard call, and therefore not guaranteed to be on all systems. On Linux, I know that if you add -D_XOPEN_SOURCE to your compiler flags, (as I have done in the accompanying Makefile) the non-ANSI compliant strptime() becomes "visible" to the compiler, and is placed in the resulting binary. On the Solaris and Irix systems that I have access to, strptime() is in sys/time.h, but it appears as though different macros have to be defined to make strptime() visible to the compiler. If you get gtkcookie to successfully compile on your favourite OS, could you please send me details of how you got it to work? Then, I can include that in future distributions of gtkcookie. My e-mail address is pq1036@110.net. Thanks! -Manni wood gtkcookie-0.4/Makefile0000644000175000017500000000303610117103364016404 0ustar mechanixmechanix00000000000000# what base directory do you want for the installation? INSTPREFIX = /usr/local # your gcc compiler CC = /usr/local/bin/gcc # define macro for gcc flags CFLAGS := -Wall -D_XOPEN_SOURCE -g $(shell pkg-config --cflags gtk+-2.0) #-D_MANNI_DEBUG # with optimisation, and without debugging information... # CFLAGS = -O3 -Wall -D_XOPEN_SOURCE `gtk-config --cflags` # define libraries to include LIBRARIES := $(shell pkg-config --libs gtk+-2.0) # statically link libraries # LIBRARIES = -static `gtk-config --libs` # electric fence -- enable static linking to ensure this works # LIBRARIES = `gtk-config --libs` -lefence INSTALL = install -o root -g root HEADERFILES = proctool.h getline.h # the default target is all:, so that if you just say "make", all: is # executed all: gtkcookie gtkcookie: gtkcookie.o proctool.o getline.o $(CC) $(CFLAGS) -o gtkcookie gtkcookie.o proctool.o getline.o $(LIBRARIES) gtkcookie.o: gtkcookie.c $(HEADERFILES) $(CC) $(CFLAGS) -c gtkcookie.c proctool.o: proctool.c $(HEADERFILES) $(CC) $(CFLAGS) -c proctool.c getline.o: getline.c $(HEADERFILES) $(CC) $(CFLAGS) -c getline.c install: gtkcookie $(INSTALL) -d -m 755 $(INSTPREFIX)/bin $(INSTALL) -s -m 755 gtkcookie $(INSTPREFIX)/bin $(INSTALL) -d -m 755 $(INSTPREFIX)/share/pixmaps $(INSTALL) -m 644 cookie.xpm $(INSTPREFIX)/share/pixmaps $(INSTALL) -m 644 small_cookie.xpm $(INSTPREFIX)/share/pixmaps $(INSTALL) -d -m 755 $(INSTPREFIX)/share/man/man1 $(INSTALL) -m 644 gtkcookie.1 $(INSTPREFIX)/share/man/man1 clean: rm -f gtkcookie rm -f *.o rm -f *~ gtkcookie-0.4/WISHLIST0000644000175000017500000000233310117036321016132 0ustar mechanixmechanix00000000000000 A wish list of things I'd like to do with gtkcookie, in no paritular order. - The same way gtkcookie looks for a running netscape, it should also look for a running gtkcookie, because I'd prefer gtkcookie to only one as one instance per user. - should all app preferences be kept in a global struct, instead of a smattering of global vars? - should all global vars be listed in the code before the function defs? - "find" should ask to start finding back at the top after not found - to the File menu, add a submenu to New, and have new Cookie file... and new Cookie... - when you cancel an edit on a new cookie, make the whole cookie disappear? - date edit field should have dropdown menus for all the parts of the date, which will ensure better construction of the date string that we convert to number of seconds since the epoch - perhaps a submenu in Cookie menu called Sort By, and then a menu item for each cookie column? - autoconf! - get the file open/save-as dialogues to show dot files and dot directories - online help - a toolbar - prevent a double-click in the scrollbars of the clist widget from popping up the edit cookie dialogue box - Check all items in this wishlist and see which are still valid ;-) (Filip) gtkcookie-0.4/cookie.xpm0000644000175000017500000001660406624707100016756 0ustar mechanixmechanix00000000000000/* XPM */ static char *cookiepic[] = { /* width height num_colors chars_per_pixel */ " 50 50 137 2", /* colors */ ".. s mask c none", ".# c #e7d6c6", ".a c #fff7ef", ".b c #decebd", ".c c #dec6ad", ".d c #7b4200", ".e c #b5946b", ".f c #844a00", ".g c #cebda5", ".h c #dec6a5", ".i c #ceb594", ".j c #c6ad8c", ".k c #9c7339", ".l c #946b31", ".m c #845210", ".n c #734200", ".o c #8c5200", ".p c #e7d6bd", ".q c #b59463", ".r c #9c6b21", ".s c #8c5a10", ".t c #845208", ".u c #7b4a00", ".v c #945a00", ".w c #deceb5", ".x c #d6c6ad", ".y c #d6bd94", ".z c #c6ad84", ".A c #b58c4a", ".B c #ad8442", ".C c #9c7331", ".D c #8c6321", ".E c #a57321", ".F c #946310", ".G c #6b4200", ".H c #845200", ".I c #bda57b", ".J c #c6a56b", ".K c #ad8c52", ".L c #a5844a", ".M c #a57b31", ".N c #946b21", ".O c #8c6318", ".P c #9c6b10", ".Q c #7b5208", ".R c #946308", ".S c #5a3900", ".T c #734a00", ".U c #8c5a00", ".V c #9c6300", ".W c #ded6c6", ".X c #efdebd", ".Y c #e7d6b5", ".Z c #decead", ".0 c #d6c6a5", ".1 c #cebd9c", ".2 c #c6ad7b", ".3 c #bda573", ".4 c #b59c6b", ".5 c #ad9463", ".6 c #a58442", ".7 c #9c7b39", ".8 c #a57b29", ".9 c #312100", "#. c #4a3100", "## c #634200", "#a c #7b5200", "#b c #946300", "#c c #ad7300", "#d c #f7efde", "#e c #b59c63", "#f c #ad945a", "#g c #c6a55a", "#h c #b5944a", "#i c #ad8c42", "#j c #ad8429", "#k c #523900", "#l c #6b4a00", "#m c #845a00", "#n c #9c6b00", "#o c #a57300", "#p c #e7d6ad", "#q c #decea5", "#r c #d6c69c", "#s c #cebd94", "#t c #c6b58c", "#u c #bda56b", "#v c #c6ad6b", "#w c #cead5a", "#x c #bd9c4a", "#y c #b59442", "#z c #ad8c39", "#A c #bd9429", "#B c #9c7310", "#C c #392900", "#D c #735200", "#E c #8c6300", "#F c #946b00", "#G c #ad7b00", "#H c #e7dec6", "#I c #e7d6a5", "#J c #bda563", "#K c #ceb56b", "#L c #c6a54a", "#M c #bd9421", "#N c #ad8410", "#O c #423100", "#P c #5a4200", "#Q c #634a00", "#R c #7b5a00", "#S c #9c7300", "#T c #a57b00", "#U c #b58400", "#V c #f7efd6", "#W c #ded6bd", "#X c #dece9c", "#Y c #d6bd6b", "#Z c #c6a542", "#0 c #bd9c31", "#1 c #b58c10", "#2 c #b58c08", "#3 c #4a3900", "#4 c #6b5200", "#5 c #846300", "#6 c #8c6b00", "#7 c #ad8400", "#8 c #b59421", "#9 c #735a00", "a. c #947300", "a# c #ded6b5", "aa c #d6cead", "ab c #e7deb5", "ac c #524200", "ad c #312900", "ae c #5a4a00", "af c #393100", "ag c #423900", /* pixels */ "....................................................................................................", "....................................................................................................", "....................................................................................................", "......................................#V#q#Y#Z#A#2#7#2#M#Z#K#X#V....................................", "..................................#I#w#1#7#7#7#U#7#7#7#7#7#G#T#N#g#q................................", "..............................#I#L#G#7#7#7#7#U#7#7#7#G#T#T#S#T#o#o#o#x#q............................", "............................#K#7#G#7#G#7#G#G#7#7#T#T#Sa.a.#F#S#F#S#n#S#o#v..........................", "........................ab#0#G#G#7#T#G#7#G#7#G#G#Sa.#5#R#9#R#5#E#F#n#n#n#n#y.Y......................", "......................#I#A#G#7#T#T#G#T#T#G#T#T#ca.#5#4aeac#Q#9#m#E#F#n#n#n.V#j.Z....................", "....................#p#8#G#T#7#T#T#T#o#o#o#o#S#n#E#9aeafaf#3#Q#R#E#b.V#n#b.V#b.E.Z..................", "...................X#0#7#7#T#T#o#S#S#S#S#n#n#n#F#5#Dacadad#3#Q#a#m#b#b#b#b#b.v.U.M.p................", "..................#d#K#7#T#Ta.#F#6#b#F#b#b#b#b#E#m#D#Qac#3ae#l#R#m#E#b.V#b#b.U.U.U.K................", "......................#w#Ta.#E#R#R#R#m.U.U.U.U.U.U#R#D#Q#Q#l#D#a.U.U#b#E#b.U.U#m.H.H.2..............", ".......................Z#S#6#R#4#Q#4#D#a#m.H#m.U.U#m.H#a#a#R#a.H#m#m.U.v.U.v.U.H.H.u.O.#............", ".......................aa.#5#Qacac#P#l#D#a.H.o.U.U#E#m#m#m#a#a#D#a#a#m.U.U.U.U.H.u.u.u.q............", "........................#B#Rae#Oafac#l.T#a#m.U.U#b.v#E.U#m#a.T.T#l.T#a#m.U.U.H.H.u.u.u.m.#..........", "........................#J#R#4ac#k#P#l#a.H.U.U#b#b.v#E.U.H#D#l###P#Q.T#a#m.U#m.f.u.T.n.T.I..........", "........................a##m#D#l#Q#l#D#a.U.U#b#b#b#b.v.U#a.T#Q#k#3ac#l#a#m.U.H.H.u.d.n.n.k..........", "..........................#H.7#a.T.T#a#m.U#b#b#b#b.v#E#m#m#l#Pag.9#k#Q#a#m#m.U.H.u.T.d.n.n.W........", "............................#s#a#a#a.H#m.U#E.v#b#E#b.U.U#a.T#Qac#3#P#l#a#m.U.H.H.u.n.n.n.n.j........", "...............................H.H.H#m.o.U.U.U.U.U.U.U.U.H#a.T#Q###l#a#m.U.U.U.H.u.u.n.n.u.5........", "..................#H.W.........H#m.H.H#m#R#m#m#m.H#m.o.H.H#a#a#a#D#a#m.U.U.U.H.H.u.u.T.n.n.k........", ".................x.q.5.0.....y.v.U#m#m#a#D#l#D#D.u#a.H.H.H.H.u#a.H.H#m#m.U#m#m.H.u.u.T.n.n.D........", ".................z.N.D.z....#z#b.U#m#a#l#Q#P###l.T.u.u#a.u.u.u.f#a.H#m#m#a#a#a#a.u.u.n.u.n.Q........", ".................g.5#e.x....#b#b#E#m#D#Q#3ag#3#P#l.T.u.u.u.u.u#a.H.H#a#a.T#l#D.u#a.u.u.n.T.u........", ".....................W......#F#b#b#m#D#Q#3#Cag#P.G.T.T.T.n.u.u.u.H#a.T#lae#P#Q#l.T.T.u.n.u.t........", "............................#h#b.U.H#D#lac#O#k#Q.n.T.u.n.u.n.u.f#a.u#D#P#3#O#k#Q.T.u.T.u.T.D........", ".......b.1.b................#q.U.U#m#R.T#l#Q#l#l.T.T.n.T.n.n.u#a.u.u#l#P#Oaf#k#Q.T.T.u.u.u.k........", ".......4.C.3...................U#m.H.H#a#a.T#D.u.u.u.T.n.n.n.T.u.u#a#l#Qac#k#P#l.T.u.u.u.u.5........", ".....#.K.m.K.W.................U#m.H.H#m.H.H.H#a#a.u.u.n.G.n.n.T.u.u#D#l#l#l#l.T#a.u.u.u.u.j........", "......#s.4.i.................0.U.o.H.H.H#m#m.H.H.H.u.T.G.G##.G.T.u#a.u.T#D.T.T#a#a.u.u.u.u#W........", ".........................y..#h.U.U.U.H#m.o.H.H.H#a.u.G###P.S####.T.u#a.H#a#a#a.u.u.u.u.u.C..........", ".......................p#B.U.v.U.U.H.H.H#m.H.H.H#a.u#l#P#k#3#k#P.G.T.u.H.H.f.H.u.u.u.u.u.I..........", ".......................Z.U#b.U#m#m#a#m.H.H.H.u.H.u#D##acag#Cag.S#l.T#a.H#a.H.u#a.u.u.u.s.#..........", ".........................U#m#m#a#D#D#D#a#a.u#a.u.u.T#lac#Oad#Oac.G.T.u.u.f.H.u.f.u.H.u.e............", ".......................z.H#m#a#D#Q###Q#l.u#a.u.u#a.T#l#P#3#3#3#P.G.T.u.u#a.u.u#a.u.u.O.p............", "...................Z#u.F#m.H#D#Qac#3ac#l#D.u#a.u.u#D#l###P#P#P.G#l.T.u.u.f.u.H.u.H.H.z..............", "..................#h.U.U#m#a.T###.#C#3##.T#a.u#a.u.u.u.T#l.G.G.G.T.u.u.u.u.u.u.H.H.K................", "..................a#.8.U.H#a.T#Qac#k#P#l.T.u.H.u#a.u.T.T.T#l.T.T.u.u.u.u.f.H.H.H.C.p................", "..........#H#H.......1.N.H.H.T.T#l###l#l.T.u#a.u.u.u.T.u.n.T.n.u.u.u.H.H.H.H.H.r.c..................", ".........0.5.5.g.......i.N#a#a.T.T#l.T.T.u.u.u.u.u.u.u.T.u.u.u.u.u.f.H.H.o#m.8.x....................", ".........z.N.D.3.b.......g.k.u.u.u.u.u.u.u.H.u.u.u.u.u.u.u.u.u.f.H.H.H.U.U.B.w......................", ".........0.5.K.4.Iaa......#W#f.H.u#a.H#a.H.H.H.H.f#a.f#a.f.H.H.H.H.U.U.U#u..........................", "...........w.j.L.l.I...........1.6.H.H.H.H.H.H.H.H.H.H.H.H#m.o.U.U.U.A.c............................", ".............0.K.l.z...............1#f.F.U.o.U.o.U.U.U.U.U.U.U.P#e.h................................", "...............g#t.w....................#r#u#i.r.F.v.R.E.A.J.0......................................", "....................................................................................................", "....................................................................................................", "....................................................................................................", "...................................................................................................." }; gtkcookie-0.4/getline.c0000644000175000017500000000531406624707100016546 0ustar mechanixmechanix00000000000000/* getline.c --> function for getting a line of any length from a filehandle Copyright (c) 1998 Manni Wood This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Manni Wood: mwood@sig.bsh.com, pq1036@110.net */ #include #include #include #include #include #include /* defines pid_t, etc */ #include #include #include /* contains all keyboard defs */ #include /* isdigit() needed by string_contains_all_numbers */ #include "getline.h" gchar *get_line_safely(FILE *filehandle, gboolean *success) { gchar *line = NULL; size_t line_length = 0; /* number of additional chars to allocate *line when it runs out of space */ const size_t alloc_length = 1024; size_t chars_read_from_file_line = 0; gchar next_char = '\0'; while (next_char != '\n' && next_char != EOF) { next_char = fgetc(filehandle); ++chars_read_from_file_line; if (next_char != '\n' && next_char != EOF) { if (chars_read_from_file_line > line_length) { line = realloc(line, (sizeof(gchar) * line_length) + (sizeof(gchar) * alloc_length) + 1); /* line will be null if the memory wasn't correctly allocated */ if (line == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } /* don't forget to keep line_length accurate! We are pointing to line_length as one of this function's arguments, and we will need it later! */ line_length += alloc_length; } /* finally, we can append the char! */ line[chars_read_from_file_line - 1] = next_char; /* don't forget the terminating null, which we have just overwritten with next_char! The math above should always be leaving me enough memory for that one extra null character after next_char */ line[chars_read_from_file_line] = '\0'; } } /* don't forget to return whether or not we are at EOF */ *success = (next_char != EOF); /* return a pointer to the newly allocated string */ return line; } gtkcookie-0.4/getline.h0000644000175000017500000000253410117034437016552 0ustar mechanixmechanix00000000000000#ifndef GETLINE_H #define GETLINE_H /* getline.h --> function for getting a line of any length from a filehandle Copyright (c) 1998 Manni Wood This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Manni Wood: mwood@sig.bsh.com, pq1036@110.net */ #include #include #include #include #include #include #include /* read a line from the file handle filehandle, and return a pointer to an allocated null terminated array of char. Also, set success true or false depending on whether or not EOF was encountered while reading filehandle */ gchar *get_line_safely(FILE *filehandle, gboolean *success); #endif /* ifndef GETLINE_H */ gtkcookie-0.4/gtkcookie.10000644000175000017500000001117306624707100017014 0ustar mechanixmechanix00000000000000.TH gtkcookie 1 "October 1998" .SH "NAME" gtkcookie \- edit Netscape cookie file .SH "SYNOPSIS" \fBgtkcookie\fP [ \fIGtk options\fP ] .SH "DESCRIPTION" .SS "Options" \fBgtkcookie\fP supports the command flags common to all Gtk applications. There are no \fBgtkcookie\fP\-specific flags. .SS "What happens at startup" On startup, \fBgtkcookie\fP will try to find your Netscape cookie file by looking for \fB~/.netscape/cookies\fP. If \fB~/.netscape/cookies\fP is found, \fBgtkcookie\fP will load the file and show it in a multi-column list. .SS "Opening a cookie file" Regardless of whether \fBgtkcookie\fP finds your cookie file, or you have to open it manually, when you open the file, all of your Netscape cookies are displayed in whatever order Netscape wrote them into the file. .SS "Sorting a cookie file" You can sort the cookies by any column by clicking on the heading for that column. .SS "Human-readable dates" The final column is actually not stored in your cookie file, but is a translation of Netscape's native date field. Netscape stores the date as the number of seconds since 1 Jan 1970 (familiar to anyone who's spent any time on Unix), but \fBgtkcookie\fP translates those dates into human-readable expiry dates in the final column. .SS "Editing cookies" To edit a cookie, double-click on the cookie, and a cookie edit dialogue will pop up. You'll notice that the date, in seconds since the epoch (the epoch is 1 Jan 1970), is not an editable field, whereas the human-readable date is. Follow the format presented in the edit dialogue box, and as you edit the human-readable date, the expiry date in seconds since the epoch will update itself. Please note (as repeated in the bugs section below) that although dates later than 2038 are supposed to present problems, (you'll see the date in seconds since the epoch become -1) dates on or after 2036 seem to present problems. I'm still looking into this. .SS "Searching for text strings" Under the \fBEdit\fP menu, select \fBFind\fP. Type in a string or substring that you wish to find, and press the \fBFind\fP button. If the string or substring is found anywhere in a cookie, that cookie will become selected, and the view will scroll to that cookie, if necessary. Pressing \fBFind\fP again will search for the next instance, or pop up a "not found" dialogue box if the string wasn't found. In its current version, \fBgtkcookie\fP isn't yet smart enough to re-start a search from the top of the cookie list, so if you need to search from the top, hightlight the first cookie, and then do your search. .SS "Deleting cookies" Right click on a cookie, and select "Delete" from the popup menu, or click on the cookie and press "Del" on your keyboard. .SS "Creating cookies" Press the "Create Cookie" button. A cookie with dummy values will be added to the cookie list, and the "Edit Cookie" dialogue box will pop up so that you can edit the new cookie to your liking. Note that even if you press "Cancel" immediately after creating a new cookie, the new cookie, with its dummy values, will still be in the list. You'll have to delete the cookie manually. .SH "FILES" .TP \fB~/.netscape/cookies\fP The Netscape cookie file in your home directory .SH "SEE ALSO" None .SH "NOTES" None .SH "AUTHOR" Manni Wood: mwood@sig.bsh.com or pq1036@110.net .SH "BUGS" 1. The "Edit Cookie" dialogue has problems with on-the-fly conversion of human-readable dates to the number of seconds since the epoch for dates later than 2036. For some reason, despite the fact that the date is supposed to overflow in 2038, the C function strptime flubs up the conversion for dates larger than 1036. Unfortunately, this means that when you edit a cookie whose expiry date is after 2036, the edit dialogue box shows the number of seconds since the epoch as -1. There is currently no workaround to this problem, besides moving the date back 2 years. 2. Although the "find" feature is supposed to always highlight and scroll to any found item, sometimes, the item becomes highlighted, but is outside the current view. 3. The file open and save dialogues don't show directories beginning with a dot (such as \fB.netscape\fP!) but typing such directory names manually will work. 4. Double-clicking in the scroll bar will pop up the "Edit Cookie" dialogue box for the currently highlighted cookie. 5. Editing the cookie file while Netscape is running is futile, because Netscape will re-write the cookie file when you exit Netscape, based on what's in its memory, not what's in the cookie file. A popup menu in my programme warns you of a running netscape... unless you're running Netscape 4.5. Netscape 4.5 doesn't seem to create the same lock file that earlier Netscapes used to. gtkcookie-0.4/gtkcookie.c0000644000175000017500000024245610117103364017102 0ustar mechanixmechanix00000000000000/* gtkcookie.c --> gtk+ web browser cookie file editor Copyright (c) 1998 Manni Wood This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Manni Wood: mwood@sig.bsh.com, pq1036@110.net Netscape, Netscape Navigator, Netscape ONE, and the Netscape N and Ship's Wheel logos are registered trademarks of Netscape Communications Corporation in the United States and other countries. */ #include #include #include #include #include #include /* uid_t */ #include #include #include #include /* contains all keyboard defs */ #include "proctool.h" #include "getline.h" #include "cookie.xpm" #define COOKIE_COLUMNS 8 /* "The static declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being compiled." -- K & R, p. 83 */ int intcmp(const char *s1, const char *s2) { double v1, v2; v1 = atof(s1); v2 = atof(s2); if (v1 < v2) { return -1; } else if (v1 > v2) { return 1; } else { return 0; } } /* COOKIE FUNCTIONS A cookie is simply an array of pointers to gchar; each pointer to gchar holds the string value of one of the cookie fields: "Domain", - domain of the server that set the cookie "Suffix", - T/F: is the domain a suffix, or a full domain? "Path", - what path does the browser have to be in to return the cookie? "Secure", - T/F: is this cookie only sent over secure http? "Expire", - time in seconds, since the epoch, this cookie expires "Name", - name (or key) of the cookie "Value", - value of the cookie "Expire". - human-readable expiration date Please note that the final "Expire" is not kept in the cookie file, but is a human-readable version of the date held in the first "Expire". */ /* is a line of text from the cookie file a valid cookie? */ static gboolean is_a_cookie_line(gchar *line); /* split a line of text into the seven cookie fields, and place in new_cookie */ static void split_cookie_line(gchar *line, gchar *new_cookie[COOKIE_COLUMNS]); /* set a cookie's gchar pointers to NULL */ static void initialise_cookie(gchar *new_cookie[COOKIE_COLUMNS]); /* free the memory pointed to by all the gchar pointers in a cookie, and set each gchar pointer to NULL */ static void free_cookie(gchar *c[COOKIE_COLUMNS]); /* copy the first cookie's strings to the gchar ptrs of the second cookie, allocating the needed memory to do so */ static void copy_cookie(gchar *c1[COOKIE_COLUMNS], gchar *c2[COOKIE_COLUMNS]); /* copy the cookie from a particular row in the clist to c, allocating the needed memory to do so */ static void copy_clist_row_to_cookie(gint row, gchar *c[COOKIE_COLUMNS]); /* handy debugging function to print the contents of a cookie */ #ifdef _MANNI_DEBUG static void dbg_print_cookie(gchar *c[COOKIE_COLUMNS]) { printf("domain: %s\n", c[0]); printf("suffix: %s\n", c[1]); printf("path: %s\n", c[2]); printf("secure: %s\n", c[3]); printf("expire: %s\n", c[4]); printf("name: %s\n", c[5]); printf("value: \"%s\"\n", c[6]); printf("expire: %s\n", c[7]); } #endif /* CLIST FUNCTIONS Functions used to manipulate the list of cookies held in the clist in the main window. */ /* parse glbl_cookie_FILE and populate the main clist widget with the results */ static void populate_glbl_clist(void); /* clear all items from main clist */ static void clear_glbl_clist(void); /* remove a row from main clist */ static void remove_selected_row_from_glbl_clist(GtkWidget *calling_widget, gpointer func_data); /* enumerate two sorting styles: sorting by string, and sorting by integer */ typedef enum { STRING, INTEGER } SORT_STYLE; /* MANNI: the fact that I had to move the enum up here points to the fact that I prabably should move all global var declarations up above the global function declarations */ /* sort the clist using a quicksort */ static void quicksort_clist(GtkWidget *clist, SORT_STYLE sort_style, gint sort_column, gint first_index, gint last_index); /* a utility function to help quicksort_clist work */ static void partition_clist(GtkWidget *clist, SORT_STYLE sort_style, gint sort_column, gint first_index, gint *pivot_index, gint last_index); /* swap the contents of two rows in the clist */ static void swap_row_clist(GtkWidget *clist, gint row1, gint row2); /* respond to a double-click in the clist widget (by calling show_edit_dialog_box) */ static gboolean select_clist_dblclk(GtkWidget *clist, GdkEventButton *event, gpointer func_data); /* copy the cookie from the selected row to the global variable which holds the currendly-selected cookie, glbl_selected_cookie */ static void select_clist (GtkWidget *clist, gint row, gint column, GdkEventButton * bevent); /* perform a sort on the column if its title button has been clicked */ /* PLEASE NOTE that sort_by_column_glbl_clist does not sort the clist arg, but sorts glbl_clist */ static void sort_by_column_glbl_clist (GtkWidget *clist, gint column, GdkEventButton * bevent); /* inserts a new cookie in clist, and activates the edit dialog box for the new cookie.*/ static void insert_row_glbl_clist (GtkWidget *calling_widget, gpointer func_data); /* creates the actual global clist widget */ static void create_glbl_clist (GtkWidget *box1); /* copies the contents of glbl_selected_cookie into the correct row of the master clist (called by clicking "OK" on the cookie edit dialogue box), then destroys the edit dialogue box */ static void update_glbl_clist(GtkWidget *calling_widget, GtkWidget *edit_win_to_destroy); /* handle a keypress in the clist If was pressed, delete the currently selected cookie */ gint handle_keypress_in_main_win(GtkWidget *calling_widget, GdkEventKey *event, gpointer func_data); /* COOKIE EDIT DIALOGUE BOX FUNCTIONS */ /* pops up the cookie edit dialogue box */ static void show_edit_dialog_box (GtkWidget *calling_widget, gpointer func_data); /* whenever an editing change is made to any of the fields of a cookie, glbl_selected_cookie has memory re-allocated, and strings re-copied, to reflect the changes made in the dialogue box */ static void change_callback(GtkWidget *entry, gpointer func_data); /* detects text changes in the human-readable date field of the cookie edit dialogue box, and changes the original expiry date field on the fly */ static void change_time_callback(GtkWidget *entry, gpointer func_data); /* called when one of the true/false checkboxes in the cookie edit dialogue box changes state */ static void change_tf_callback(GtkWidget *toggle_button, gpointer func_data); /* handle a keypress in the edit dialogue. If was pressed, destroy the window and save the cookie If was pressed, destroy the find window and don't save the cookie. */ gint handle_keypress_in_edit_win(GtkWidget *edit_window, GdkEventKey *event, gpointer func_data); /* FIND DIALOG BOX FUNCTIONS */ /* show the 'find' dialogue box */ static void show_find_dialog_box(GtkWidget *calling_widget, gpointer func_data); static void search (GtkWidget*, gpointer); static gint search_dlg_key_cb (GtkWidget*, GdkEventKey*, gpointer); static void clear_search (GtkWidget*, gpointer); /* make glbl_last_found_row equal -1 again */ static void reset_last_found_row(GtkWidget *calling_widget, gpointer func_data); /* set case sensitive searching TRUE/FALSE */ /* (held in the global variable glbl_search_case_sensitive) */ static void set_tf_case_sensitive_search(GtkWidget *toggle_button, gpointer func_data); /* OTHER DIALOG BOX FUNCTIONS */ /* show the 'about' dialogue box */ static void show_about_dialog_box(GtkWidget *calling_widget, gpointer func_data); /* show an error dialogue box, with the following message */ static void show_error_dialog_box (gchar *message); /* FILE SELECTION AND SAVE FUNCTIONS */ /* respond to the "OK" button in the open file dialogue */ static void file_selection_ok (GtkWidget *w, GtkFileSelection *fs); /* respond to the "OK" button in the save_as dialogue box */ static void save_as_ok (GtkWidget *w, GtkFileSelection *fs); /* creates the file selection dialogue box that you see when you use "open" */ static void show_open_file_dialog_box (GtkWidget *calling_widget, gpointer func_data); /* creates the "save as" dialoge box */ static void show_save_as_dialog_box (GtkWidget *calling_widget, gpointer func_data); /* handles the save_file request from the file manu; is smart enough to either call write_file() if the file exists, or call show_save_as_dialog_box if glbl_selected_filename_str is empty */ static void save_file(GtkWidget *calling_widget, gpointer func_data); /* actually writes all of the cookie data held in the main clist out into a properly formatted mozilla file whose name should be held in glbl_selected_filename_str */ static void write_file(void); /* OTHER FUNCTIONS */ /* check to see if there is a netscape lockfile for the current user, and if so, issue a warning */ static void check_for_running_netscape(void); /* change the cursor -- seems not to be working */ /* static void set_cursor (guint c); */ /* ALL GLOBAL VARIABLES START WITH glbl_ */ /* cookie menu */ static GtkWidget *glbl_cookie_menu_item_holder; static GtkWidget *glbl_cookie_menu_items; static GtkWidget *glbl_cookie_menu; /* the main programme window */ static GtkWidget *glbl_main_window; /* title of the main programme window */ static GString *glbl_window_title_gstr; /* the master combo list */ static GtkWidget *glbl_clist; /* we need a global pointer to point to the expire time label in an edit window whenever the edit window is constructed. This will give us a handle to the label even after the function is run. We need the handle to the label so that we can change the value of the label as handler functions are called */ static GtkWidget *glbl_selected_etl; /* we need a time struct to throw the results of strptime() into */ static struct tm glbl_selected_time; /* we also need a time_t global var to convert glbl_selected_time from */ static time_t glbl_selected_time_sec; /* finally, we need an ascii string representation of the glbl_selected_time_sec. January 2038 is when a 32 bit int will roll over, and that 32 bit int seems to need 10 ascii characters to print. I'll therefore allocate 12 characters to be safe. This won't, however, be safe if we move to 64 bit ints */ static gchar glbl_selected_time_str[12]; static gint glbl_clist_rows = 0; static gint glbl_clist_selected_row = 0; static gchar *glbl_selected_cookie[COOKIE_COLUMNS]; /* the currently selected cookie in the glbl_clist */ /* glbl_selected_filename_str will be a string holding the name of the file from a file select box, or the default cookie file location */ static gchar *glbl_selected_filename_str = NULL; static gchar *glbl_home_directory_str = NULL; /* user's home directory */ /* static gchar *glbl_netscape_dot_dir_str = NULL; netscape dot directory */ static FILE *glbl_cookie_FILE = NULL; static gchar *glbl_cookie_titles_str[] = { "Domain", "Suffix", "Path", "Secure", "Expire (sec)", "Name", "Value", "Expire" }; /* enumerate a label for each column name so that we can use a column name instead of a column number. cookie[Domain] tells us more than cookie[0] */ enum COLUMN_NAMES {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; /* sad, but true: a callback function that I attach to each text entry field in the cookie popup window requires a pointer to an int as an argument, so I have to maintain this array of pointers to ints just to keep track of which of the 8 text fields is being used. */ static gint *glbl_int_ptr[COOKIE_COLUMNS]; GdkPixmap *glbl_cookie_xpm; GdkBitmap *glbl_cookie_xpm_mask; /* keeps track of the row of the last successful find */ static gint glbl_last_found_row = -1; /* keeps track of the last column that was sorted. If the value is -1, no column has been sorted */ static gint glbl_last_sorted_column = -1; static gboolean glbl_search_case_sensitive = FALSE; /* main */ int main (int argc, gchar *argv[]) { GtkWidget *file_menu_item_holder; GtkWidget *menu_bar; GtkWidget *file_menu; GtkWidget *menu_items; GtkWidget *vbox; GtkWidget *help_menu_item_holder; GtkWidget *help_menu_items; GtkWidget *help_menu; GtkWidget *edit_menu_item_holder; GtkWidget *edit_menu_items; GtkWidget *edit_menu; GdkColor *transparent = NULL; GtkAccelGroup* accel_group; int i; gtk_init (&argc, &argv); /* initialise the pointers to ints */ for (i = 0; i < COOKIE_COLUMNS; i++) { glbl_int_ptr[i] = malloc(sizeof(int)); *glbl_int_ptr[i] = i; } /* initialise the window title */ glbl_window_title_gstr = g_string_new("gtkcookie"); /* create a new glbl_main_windoindw */ glbl_main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_usize( GTK_WIDGET (glbl_main_window), 700, 300); gtk_window_set_title(GTK_WINDOW (glbl_main_window), glbl_window_title_gstr->str); gtk_signal_connect(GTK_OBJECT (glbl_main_window), "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); /* handle any keypresses in the clist. For now, we only care about Del, but that could change. */ gtk_signal_connect(GTK_OBJECT(glbl_main_window), "key_press_event", GTK_SIGNAL_FUNC(handle_keypress_in_main_win), NULL); /* add the icon to the glbl_main_window */ gtk_widget_realize(glbl_main_window); glbl_cookie_xpm = gdk_pixmap_create_from_xpm_d (glbl_main_window->window, &glbl_cookie_xpm_mask, transparent, cookiepic); gdk_window_set_icon(glbl_main_window->window, NULL, glbl_cookie_xpm, glbl_cookie_xpm); /* add accelerator_table to the window */ accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(glbl_main_window), accel_group); /* FILE MENU */ /* Init the menu-widget, and remember -- never * gtk_show_widget() the file_menu_item_holder widget!! * This is the menu that holds the menu items, the one that * will pop up when you click on the "File" menu in the app */ file_menu_item_holder = gtk_menu_new(); /* Next we make the menu-entries for "file_menu_item_holder". * Notice the call to gtk_menu_append. Here we are adding a list of * menu items to our menu. Normally, we'd also catch the "clicked" * signal on each of the menu items and setup a callback for it, * but it's omitted here to save space. */ /***************/ /* Create a new menu-item with a name... */ menu_items = gtk_menu_item_new_with_label("Open..."); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU (file_menu_item_holder), menu_items); /* Do something interesting when the menuitem is selected */ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate", GTK_SIGNAL_FUNC(show_open_file_dialog_box), NULL); /* install the accelerator */ gtk_widget_add_accelerator(menu_items, "activate", accel_group, 'O', GDK_CONTROL_MASK, 0); /* Show the widget */ gtk_widget_show(menu_items); /***************/ /***************/ /* Create a new menu-item with a name... */ menu_items = gtk_menu_item_new_with_label("Save"); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU (file_menu_item_holder), menu_items); /* Do something interesting when the menuitem is selected */ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate", GTK_SIGNAL_FUNC(save_file), NULL); /* install the accelerator */ gtk_widget_add_accelerator(menu_items, "activate", accel_group, 'S', GDK_CONTROL_MASK, 0); /* Show the widget */ gtk_widget_show(menu_items); /***************/ /***************/ /* Create a new menu-item with a name... */ menu_items = gtk_menu_item_new_with_label("Save as..."); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU (file_menu_item_holder), menu_items); /* Do something interesting when the menuitem is selected */ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate", GTK_SIGNAL_FUNC(show_save_as_dialog_box), NULL); /* Show the widget */ gtk_widget_show(menu_items); /***************/ /***************/ /* Create a new blank menu-item (which creates a line ... */ menu_items = gtk_menu_item_new(); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU(file_menu_item_holder), menu_items); /* no need to attach a function to a separator... */ /* Show the widget */ gtk_widget_show(menu_items); /***************/ /***************/ /* Create a new menu-item with a name... */ menu_items = gtk_menu_item_new_with_label("Quit"); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU (file_menu_item_holder), menu_items); /* quit when the "Exit" menuitem is selected */ gtk_signal_connect(GTK_OBJECT (menu_items), "activate", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); /* install the accelerator */ gtk_widget_add_accelerator(menu_items, "activate", accel_group, 'Q', GDK_CONTROL_MASK, 0); /* Show the widget */ gtk_widget_show(menu_items); /***************/ /* This is the file menu, and will be the label * displayed on the menu bar. There won't be a signal handler attached, * as it only pops up the rest of the menu when pressed. */ file_menu = gtk_menu_item_new_with_label("File"); gtk_widget_show(file_menu); /* Now we specify that we want our newly created "file_menu_item_holder" to be the menu * for the "file menu" */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (file_menu), file_menu_item_holder); /* COOKIE MENU */ glbl_cookie_menu_item_holder = gtk_menu_new(); /********/ glbl_cookie_menu_items = gtk_menu_item_new_with_label("New..."); gtk_menu_append(GTK_MENU (glbl_cookie_menu_item_holder), glbl_cookie_menu_items); gtk_signal_connect_object(GTK_OBJECT(glbl_cookie_menu_items), "activate", GTK_SIGNAL_FUNC(insert_row_glbl_clist), NULL); /* install the accelerator */ gtk_widget_add_accelerator(glbl_cookie_menu_items, "activate", accel_group, 'N', GDK_CONTROL_MASK, 0); gtk_widget_show(glbl_cookie_menu_items); /*********/ /********/ glbl_cookie_menu_items = gtk_menu_item_new_with_label("Edit..."); gtk_menu_append(GTK_MENU (glbl_cookie_menu_item_holder), glbl_cookie_menu_items); gtk_signal_connect_object(GTK_OBJECT(glbl_cookie_menu_items), "activate", GTK_SIGNAL_FUNC(show_edit_dialog_box), NULL); /* install the accelerator */ gtk_widget_add_accelerator(glbl_cookie_menu_items, "activate", accel_group, 'E', GDK_CONTROL_MASK, 0); gtk_widget_show(glbl_cookie_menu_items); /*********/ /********/ glbl_cookie_menu_items = gtk_menu_item_new_with_label("Delete"); gtk_menu_append(GTK_MENU (glbl_cookie_menu_item_holder), glbl_cookie_menu_items); gtk_signal_connect_object(GTK_OBJECT(glbl_cookie_menu_items), "activate", GTK_SIGNAL_FUNC(remove_selected_row_from_glbl_clist), NULL); /* add "Del" to this one. It won't work as a keyboard accelerator, so find another way */ gtk_widget_show(glbl_cookie_menu_items); /*********/ glbl_cookie_menu = gtk_menu_item_new_with_label("Cookie"); gtk_widget_show(glbl_cookie_menu); gtk_menu_item_set_submenu(GTK_MENU_ITEM (glbl_cookie_menu), glbl_cookie_menu_item_holder); /* HELP MENU */ /* Init the menu-widget, and remember -- never * gtk_show_widget() the file_menu_item_holder widget!! * This is the menu that holds the menu items, the one that * will pop up when you click on the "File" menu in the app */ help_menu_item_holder = gtk_menu_new(); /* Next we make the menu-entries for "file_menu_item_holder". * Notice the call to gtk_menu_append. Here we are adding a list of * menu items to our menu. Normally, we'd also catch the "clicked" * signal on each of the menu items and setup a callback for it, * but it's omitted here to save space. */ /***************/ /* Create a new menu-item with a name... */ help_menu_items = gtk_menu_item_new_with_label("About..."); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU (help_menu_item_holder), help_menu_items); /* Do something interesting when the menuitem is selected */ gtk_signal_connect_object(GTK_OBJECT(help_menu_items), "activate", GTK_SIGNAL_FUNC(show_about_dialog_box), NULL); /* Show the widget */ gtk_widget_show(help_menu_items); /***************/ /* This is the file menu, and will be the label * displayed on the menu bar. There won't be a signal handler attached, * as it only pops up the rest of the menu when pressed. */ help_menu = gtk_menu_item_new_with_label("Help"); gtk_widget_show(help_menu); /* Now we specify that we want our newly created "file_menu_item_holder" to be the menu * for the "file menu" */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (help_menu), help_menu_item_holder); /* /HELP MENU */ /* EDIT MENU */ /* Init the menu-widget, and remember -- never * gtk_show_widget() the file_menu_item_holder widget!! * This is the menu that holds the menu items, the one that * will pop up when you click on the "File" menu in the app */ edit_menu_item_holder = gtk_menu_new(); /* Next we make the menu-entries for "file_menu_item_holder". * Notice the call to gtk_menu_append. Here we are adding a list of * menu items to our menu. Normally, we'd also catch the "clicked" * signal on each of the menu items and setup a callback for it, * but it's omitted here to save space. */ /***************/ /* Create a new menu-item with a name... */ edit_menu_items = gtk_menu_item_new_with_label("Find..."); /* ...and add it to the file_menu_item_holder. */ gtk_menu_append(GTK_MENU (edit_menu_item_holder), edit_menu_items); /* Do something interesting when the menuitem is selected */ gtk_signal_connect_object(GTK_OBJECT(edit_menu_items), "activate", GTK_SIGNAL_FUNC(show_find_dialog_box), NULL); /* install the accelerator */ gtk_widget_add_accelerator(edit_menu_items, "activate", accel_group, 'F', GDK_CONTROL_MASK, 0); /* Show the widget */ gtk_widget_show(edit_menu_items); /***************/ /* This is the file menu, and will be the label * displayed on the menu bar. There won't be a signal handler attached, * as it only pops up the rest of the menu when pressed. */ edit_menu = gtk_menu_item_new_with_label("Edit"); gtk_widget_show(edit_menu); /* Now we specify that we want our newly created "file_menu_item_holder" to be the menu * for the "file menu" */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (edit_menu), edit_menu_item_holder); /* /EDIT MENU */ /* VBOX */ /* A vbox to put a menu and a button in: */ vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(glbl_main_window), vbox); gtk_widget_show(vbox); /* Create a menu-bar to hold the menus and add it to our main glbl_main_window */ menu_bar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2); gtk_widget_show(menu_bar); /* embed the glbl_clist here */ create_glbl_clist(vbox); /* And finally we append the menu-item to the menu-bar -- this is the * "File" menu-item I have been raving about =) */ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), file_menu); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), edit_menu); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), glbl_cookie_menu); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), help_menu); /* always display the glbl_main_window as the last step so it all splashes on the screen at once. */ gtk_widget_show(glbl_main_window); /* fill the glbl_clist widget with all of the cookies */ populate_glbl_clist(); /* after populating the clist, see if there is a netscape lock file, and be good enough to let the user know about it */ check_for_running_netscape(); gtk_main (); /* free the memory from the pointers to ints */ for (i = 0; i < COOKIE_COLUMNS; i++) { free(glbl_int_ptr[i]); } return 0; } static void save_file(GtkWidget *calling_widget, gpointer func_data) { if (glbl_selected_filename_str == NULL) { /* show_save_as_dialog_box, when called as a callback, takes a pointer to the calling widget, and a pointer to extra function data, if required */ show_save_as_dialog_box(NULL, NULL); } else { write_file(); } } static void write_file (void) { GString *error_msg_gstr; gchar *cell_contents; int i; int row; /* set the title of the main window to show the selected filename */ /* first, erase what's currently in the gstring */ g_string_erase(glbl_window_title_gstr, 0, glbl_window_title_gstr->len); /* now create the new title, always starting with "gtkcookie - " and then the filename */ g_string_assign(glbl_window_title_gstr, "gtkcookie - "); g_string_append(glbl_window_title_gstr, glbl_selected_filename_str); /* set the title of the main window */ gtk_window_set_title(GTK_WINDOW (glbl_main_window), glbl_window_title_gstr->str); glbl_cookie_FILE = fopen(glbl_selected_filename_str, "w"); if (glbl_cookie_FILE == NULL) { error_msg_gstr = g_string_new(""); g_string_sprintf(error_msg_gstr, "Can't write the cookie file\n\"%s\".\n(Probably not allowed to write\nto this directory, or\nthe filesystem is full.)", glbl_selected_filename_str); show_error_dialog_box(error_msg_gstr->str); /* don't forget to deallocate the string after we're done with it! Fortunately, deallocating the string seems not to harm the error message created by show_error_dialog_box() */ g_string_free(error_msg_gstr, TRUE); return; } fprintf(glbl_cookie_FILE, "# Netscape HTTP Cookie File\n"); fprintf(glbl_cookie_FILE, "# http://www.netscape.com/newsref/std/cookie_spec.html\n"); fprintf(glbl_cookie_FILE, "# This is a generated file! Do not edit.\n\n"); for (row = 0; row < glbl_clist_rows; row++) { for (i = 0; i < (COOKIE_COLUMNS - 1); i++) { gtk_clist_get_text(GTK_CLIST (glbl_clist), row, i, &cell_contents); if (i < (COOKIE_COLUMNS - 2)) { fprintf(glbl_cookie_FILE, "%s\t", cell_contents); } else { fprintf(glbl_cookie_FILE, "%s\n", cell_contents); } } } fclose(glbl_cookie_FILE); } static void show_about_dialog_box(GtkWidget *calling_widget, gpointer func_data) { static GtkWidget *about_window = NULL; GtkWidget *box1; GtkWidget *about_text_label; if (!about_window) { GtkWidget *btn; about_window = gtk_dialog_new(); gtk_dialog_set_has_separator (GTK_DIALOG (about_window), FALSE); gtk_window_position(GTK_WINDOW(about_window), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT(about_window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &about_window); gtk_window_set_title (GTK_WINDOW (about_window), "About gtkcookie"); gtk_container_border_width (GTK_CONTAINER (about_window), 0); box1 = gtk_vbox_new (FALSE, 10); gtk_container_set_border_width (GTK_CONTAINER (box1), 10); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (about_window)->vbox), box1); gtk_widget_show (box1); about_text_label = gtk_label_new("gtkcookie, version 0.03\n\nCode copyright (c) 1998 Manni Wood.\nCookie icons copyright (c) 1998 Glenn Matheson.\n\ngtkcookie comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome\nto redistribute it under certain conditions;\nfor details, see the file named 'COPYING' that\ncame with the source code of this programme.\n\nNetscape is a registered trademark of\nNetscape Communications Corporation\nin the United States and other countries."); gtk_box_pack_start (GTK_BOX (box1), about_text_label, TRUE, TRUE, 0); gtk_widget_show (about_text_label); btn = gtk_dialog_add_button (GTK_DIALOG (about_window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); g_signal_connect_swapped (G_OBJECT (btn), "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (about_window)); gtk_widget_grab_default (btn); gtk_widget_show (btn); } if (!GTK_WIDGET_VISIBLE (about_window)) { gtk_widget_show (about_window); } else { gtk_widget_destroy (about_window); } } static void show_find_dialog_box (GtkWidget* wdg, gpointer data) { static GtkWidget *search_dlg = NULL; GtkWidget *box1; GtkWidget *hbox2; GtkWidget *hbox1; GtkWidget *find_text_label; GtkWidget *case_sensitive_tf_checkbox; /* each time the find dialog box is opened, set glbl_last_found_row to zero, to indicate that whatever the user is about to search on, it is the first time he will be searching for that string */ glbl_last_found_row = -1; if (!search_dlg) { GtkWidget *btn, *needle; search_dlg = gtk_dialog_new(); gtk_window_position (GTK_WINDOW (search_dlg), GTK_WIN_POS_MOUSE); g_signal_connect (G_OBJECT (search_dlg), "destroy", G_CALLBACK (gtk_widget_destroyed), &search_dlg); /* REALLY IMPORTANT NOTE! gtk_widget_destroyed (notice the past tense) seems to do something a whole lot different than gtk_widget_destroy (notice the present tense) */ gtk_window_set_title (GTK_WINDOW (search_dlg), _("Find cookie")); box1 = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (search_dlg)->vbox), box1); gtk_widget_show (box1); hbox1 = gtk_hbox_new (FALSE, 10); gtk_container_border_width (GTK_CONTAINER (hbox1), 10); gtk_box_pack_start (GTK_BOX (box1), hbox1, TRUE, TRUE, 0); gtk_widget_show (hbox1); find_text_label = gtk_label_new("Find:"); gtk_box_pack_start (GTK_BOX (hbox1), find_text_label, TRUE, TRUE, 0); gtk_widget_show (find_text_label); needle = gtk_entry_new(); g_signal_connect (G_OBJECT (needle), "changed", G_CALLBACK (reset_last_found_row), NULL); gtk_box_pack_start (GTK_BOX (hbox1), needle, TRUE, TRUE, 0); gtk_widget_grab_focus (needle); gtk_widget_show (needle); hbox2 = gtk_hbox_new (FALSE, 10); gtk_container_border_width (GTK_CONTAINER (hbox2), 10); gtk_box_pack_start (GTK_BOX (box1), hbox2, FALSE, TRUE, 0); gtk_widget_show (hbox2); case_sensitive_tf_checkbox = gtk_check_button_new_with_label ("Case sensitive"); gtk_signal_connect(GTK_OBJECT(case_sensitive_tf_checkbox), "clicked", GTK_SIGNAL_FUNC(set_tf_case_sensitive_search), NULL); /* turn the button on or off depending on whether the glbl_search_case_sensitive is "TRUE" or "FALSE" */ if (glbl_search_case_sensitive == TRUE) { /* checkbox is checked */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(case_sensitive_tf_checkbox), TRUE ); } else { /* checkbox is unchecked */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(case_sensitive_tf_checkbox), FALSE ); } gtk_box_pack_start (GTK_BOX (hbox2), case_sensitive_tf_checkbox, FALSE, TRUE, 0); gtk_widget_show (case_sensitive_tf_checkbox); /********************/ btn = gtk_dialog_add_button (GTK_DIALOG (search_dlg), GTK_STOCK_FIND, GTK_RESPONSE_ACCEPT); g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (search), needle); gtk_widget_show (btn); btn = gtk_dialog_add_button (GTK_DIALOG (search_dlg), GTK_STOCK_CLEAR, GTK_RESPONSE_REJECT); g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (clear_search), needle); gtk_widget_show (btn); btn = gtk_dialog_add_button (GTK_DIALOG (search_dlg), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); g_signal_connect_swapped (G_OBJECT (btn), "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (search_dlg)); gtk_widget_show (btn); /* check if the user hit Return and if so, call search() */ g_signal_connect (G_OBJECT (search_dlg), "key_press_event", G_CALLBACK (search_dlg_key_cb), needle); } if (!GTK_WIDGET_VISIBLE (search_dlg)) { gtk_widget_show (search_dlg); } else { gtk_widget_destroy (search_dlg); } } static void show_error_dialog_box (gchar *message) { static GtkWidget *error_window = NULL; GtkWidget *box1; GtkWidget *about_text_label; if (!error_window) { GtkWidget *btn; error_window = gtk_dialog_new(); gtk_dialog_set_has_separator (GTK_DIALOG (error_window), FALSE); gtk_window_position(GTK_WINDOW(error_window), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (error_window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &error_window); gtk_window_set_title (GTK_WINDOW (error_window), "Error"); gtk_container_border_width (GTK_CONTAINER (error_window), 0); box1 = gtk_vbox_new (FALSE, 10); gtk_container_border_width (GTK_CONTAINER (box1), 10); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (error_window)->vbox), box1); gtk_widget_show (box1); about_text_label = gtk_label_new(message); gtk_box_pack_start (GTK_BOX (box1), about_text_label, TRUE, TRUE, 0); gtk_widget_show (about_text_label); btn = gtk_dialog_add_button (GTK_DIALOG (error_window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); g_signal_connect_swapped (G_OBJECT (btn), "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (error_window)); gtk_widget_grab_default (btn); gtk_widget_show (btn); } if (!GTK_WIDGET_VISIBLE (error_window)) { /* gtk_widget_realize (error_window); */ gtk_grab_add (error_window); gtk_widget_show (error_window); /* gdk_window_raise (error_window->window); */ } else { gtk_grab_remove (error_window); gtk_widget_destroy (error_window); } } static void clear_glbl_clist(void) { gtk_clist_clear (GTK_CLIST (glbl_clist)); glbl_clist_rows = 0; } static void remove_selected_row_from_glbl_clist(GtkWidget *calling_widget, gpointer func_data) { gtk_clist_remove (GTK_CLIST (glbl_clist), glbl_clist_selected_row); glbl_clist_rows--; } static void sort_by_column_glbl_clist (GtkWidget *clist, gint sort_column, GdkEventButton * bevent) { /* if the sort_column is the human readable date, make sort_comumn equal the numeric date column, because that's what we really want to sort the date on anyway. */ if (sort_column == COL_EXPIRE_DATE) { sort_column = COL_EXPIRE_SECONDS; } /* as a small but useful optimisation, let us check to see if the sort_column we are about to sort on has already been sorted. If so, we can return right now. */ if (sort_column == glbl_last_sorted_column) { return; } /* find a way to make the icon a watch */ /* set_cursor(150); seems not to be working */ /* I freeze graphical updates to the clist while the sort takes place */ gtk_clist_freeze(GTK_CLIST (glbl_clist)); if (sort_column == COL_EXPIRE_SECONDS) { quicksort_clist(glbl_clist, INTEGER, sort_column, 0, (glbl_clist_rows - 1)); } else { quicksort_clist(glbl_clist, STRING, sort_column, 0, (glbl_clist_rows - 1)); } gtk_clist_thaw(GTK_CLIST (glbl_clist)); /* find a way to turn the icon from a watch to a pointer */ /* set_cursor(132); seems not to be working */ glbl_last_sorted_column = sort_column; /* finally, make sure the selected and highlighted cookie is the first one! */ select_clist(glbl_clist, 0, 0, NULL); } static void swap_row_clist(GtkWidget *clist, gint row1, gint row2) { gchar *tmp_cookie[COOKIE_COLUMNS]; gchar *cell_contents; gint col; initialise_cookie(tmp_cookie); /* always do this with a new cookie! */ /* if row1 and row2 are the same, then there is no work to be done */ if (row1 == row2) { return; } copy_clist_row_to_cookie(row1, tmp_cookie); for (col = 0; col < COOKIE_COLUMNS; col++) { gtk_clist_get_text(GTK_CLIST (clist), row2, col, &cell_contents); gtk_clist_set_text(GTK_CLIST (clist), row1, col, cell_contents); } for (col = 0; col < COOKIE_COLUMNS; col++) { gtk_clist_set_text(GTK_CLIST (clist), row2, col, tmp_cookie[col]); } /* free the memory used by the cookie before leaving subroutine! */ free_cookie(tmp_cookie); } static gboolean select_clist_dblclk(GtkWidget *clist, GdkEventButton *event, gpointer func_data) { gint row; row = glbl_clist_selected_row; if (GTK_IS_CLIST(clist) && event->type == GDK_2BUTTON_PRESS) { show_edit_dialog_box(clist, func_data); } return FALSE; } static void select_clist (GtkWidget *clist, gint row, gint column, GdkEventButton * bevent) { gchar *tmp_cookie[COOKIE_COLUMNS]; gint i; /* initialise all of glbl_selected_cookie's gchar pointers to NULL */ initialise_cookie(tmp_cookie); for (i = 0; i < COOKIE_COLUMNS; i++) { switch (gtk_clist_get_cell_type (GTK_CLIST (clist), row, i)) { case GTK_CELL_TEXT: gtk_clist_get_text(GTK_CLIST (clist), row, i, &tmp_cookie[i]); break; default: break; } } /* tmp_cookie contains pointers to the strings in the clist, but I need a copy of the cookie whose contents I can modify without messing with the text in the glbl_clist. Hence, I copy tmp_cookie's strings to the global var, glbl_selected_cookie */ copy_cookie(tmp_cookie, glbl_selected_cookie); /* IMPORTANT! set the selected row to the selected row */ glbl_clist_selected_row = row; /* if right-clicked (ie, button 3), pop up the cookie menu */ /* note that I'm checking if bevent is NULL, because sometimes I call this function directly (instead if it being called by an event) and therefore just make the last argument (bevent) NULL. Checking bevent->button on NULL crashes the programme. */ if (bevent != NULL && bevent->button == 3) { gtk_menu_popup (GTK_MENU(glbl_cookie_menu_item_holder), NULL, NULL, NULL, NULL, bevent->button, bevent->time); } } static void insert_row_glbl_clist (GtkWidget *calling_widget, gpointer func_data) { /* future: 1. clear the currently selected cookie 2. gtk_clist_insert the currently selected cookie 3. make the just-inserted cookie the selected cookie in the glbl_clist (ensure all of the global "this is the selected cookie row" variables reflect this change) 4. scroll the glbl_clist so that the new, selected cookie is visible 5. call the double-click function that brings up the cookie edit window */ static gchar *text[] = { "url.com", "TRUE", "/", "FALSE", "0", "key", "value", "Thu Jan 1 00:00:00 1970" }; gtk_clist_insert (GTK_CLIST(glbl_clist), glbl_clist_selected_row, text); glbl_clist_rows++; glbl_last_sorted_column = -1; /* because we just added a new row, no columns can be assumed to be sorted */ gtk_clist_select_row(GTK_CLIST(glbl_clist), glbl_clist_selected_row, 0 ); show_edit_dialog_box(calling_widget, func_data); } static void create_glbl_clist (GtkWidget *box1) { /* ScrolledWindow to be put around glbl_clist */ GtkWidget* sw_clist; /* create GtkCList here so we have a pointer to throw at the * button callbacks -- more is done with it later */ glbl_clist = gtk_clist_new_with_titles (COOKIE_COLUMNS, glbl_cookie_titles_str); /* * the rest of the glbl_clist configuration */ gtk_clist_set_row_height (GTK_CLIST (glbl_clist), 20); /* 20 pixels high for a single row? */ /* if the user clicks on a column title button... */ gtk_signal_connect (GTK_OBJECT (glbl_clist), "click_column", GTK_SIGNAL_FUNC(sort_by_column_glbl_clist), NULL); /* if the user clicks on a row... */ gtk_signal_connect (GTK_OBJECT (glbl_clist), "select_row", GTK_SIGNAL_FUNC(select_clist), NULL); /* if the user double-clicks on a row... */ gtk_signal_connect(GTK_OBJECT(glbl_clist), "button_press_event", GTK_SIGNAL_FUNC(select_clist_dblclk), NULL); /* set the width and justification of each cookie column */ gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_DOMAIN, 100); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_SUFFIX, 50); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_PATH, 50); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_SECURE, 50); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_EXPIRE_SECONDS, 80); gtk_clist_set_column_justification(GTK_CLIST(glbl_clist), COL_EXPIRE_SECONDS, GTK_JUSTIFY_RIGHT); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_NAME, 80); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_VALUE, 80); gtk_clist_set_column_width(GTK_CLIST(glbl_clist), COL_EXPIRE_DATE, 150); /* WIDTH HEIGHT */ /* gtk_widget_set_usize(glbl_clist, width, height); */ /* but first check to see if there is a window width/height */ /* what is a selection mode? */ gtk_clist_set_selection_mode (GTK_CLIST (glbl_clist), GTK_SELECTION_BROWSE); /* what is a policy? */ sw_clist = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw_clist), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(sw_clist), glbl_clist); gtk_container_border_width (GTK_CONTAINER (glbl_clist), 0); gtk_box_pack_start (GTK_BOX (box1), sw_clist, TRUE, TRUE, 0); gtk_widget_show (glbl_clist); gtk_widget_show(sw_clist); } /* POPULATE GLBL_CLIST */ static void populate_glbl_clist(void) { gchar *file_line = NULL; /* were we successful reading a line from the file? */ gboolean success = FALSE; gchar *test_cookie[COOKIE_COLUMNS]; GString *error_msg_gstr; size_t pathname_str_length; glbl_clist_rows = 0; /* seeing as we are starting fresh, the total number of rows in the glbl_clist widget is 0 */ glbl_last_sorted_column = -1; /* also, because we are starting fresh, the last sorted column becomes unknown (-1) again */ /* set all gchar pointers in test_cookie to zero */ initialise_cookie(test_cookie); /* if the selected filename is NULL, then we look in the default location for the cookie file */ if (glbl_selected_filename_str == NULL) { glbl_home_directory_str = getenv("HOME"); if (glbl_home_directory_str == NULL) { show_error_dialog_box ("Environmental variable HOME not set;\ntherefore, can't find home directory\nor .netscape directory."); return; } pathname_str_length = strlen(glbl_home_directory_str) + strlen("/.netscape/cookies"); glbl_selected_filename_str = calloc(pathname_str_length + 1, sizeof(gchar)); strncpy(glbl_selected_filename_str, glbl_home_directory_str, strlen(glbl_home_directory_str)); strcat(glbl_selected_filename_str, "/.netscape/cookies"); glbl_selected_filename_str[pathname_str_length] = '\0'; } /* set the title of the main window to show the selected filename */ /* first, erase what's currently in the gstring */ g_string_erase(glbl_window_title_gstr, 0, glbl_window_title_gstr->len); /* now create the new title, always starting with "gtkcookie - " and then the filename */ g_string_assign(glbl_window_title_gstr, "gtkcookie - "); g_string_append(glbl_window_title_gstr, glbl_selected_filename_str); /* set the title of the main window */ gtk_window_set_title(GTK_WINDOW (glbl_main_window), glbl_window_title_gstr->str); glbl_cookie_FILE = fopen(glbl_selected_filename_str, "r"); if (glbl_cookie_FILE == NULL) { /* if we couldn't open the cookie file, set glbl_selected_filename_str to NULL so that if the user says 'save', a 'save as' will be invoked instead. */ if (glbl_selected_filename_str != NULL) { free(glbl_selected_filename_str); glbl_selected_filename_str = NULL; } error_msg_gstr = g_string_new(""); g_string_sprintf(error_msg_gstr, "can't open the cookie file\n\"%s\"\nfor reading.", glbl_selected_filename_str); show_error_dialog_box(error_msg_gstr->str); /* DEALLOCATE! */ g_string_free(error_msg_gstr, TRUE); return; } file_line = get_line_safely(glbl_cookie_FILE, &success); if (success && is_a_cookie_line(file_line)) { split_cookie_line(file_line, test_cookie); gtk_clist_append(GTK_CLIST(glbl_clist), test_cookie); glbl_clist_rows++; /* do this whenever you gtk_clist_append() */ free_cookie(test_cookie); } free(file_line); file_line = NULL; while (success) { file_line = get_line_safely(glbl_cookie_FILE, &success); if (success && is_a_cookie_line(file_line)) { split_cookie_line(file_line, test_cookie); gtk_clist_append(GTK_CLIST(glbl_clist), test_cookie); glbl_clist_rows++; /* do this whenever you gtk_clist_append() */ free_cookie(test_cookie); } free(file_line); file_line = NULL; } fclose(glbl_cookie_FILE); } /* COOKIE FUNCTIONS */ static gboolean is_a_cookie_line(gchar *line) { /* a valid cookie line has 6 tabs */ int tab_count; size_t i; /* always deal with the possibility of a null string! */ if (line == NULL) { return FALSE; } tab_count = 0; for (i = 0; i < strlen(line); i++) { if (line[i] == '\t') { tab_count++; } } if (tab_count == 6) { return TRUE; } else { return FALSE; } } /* make sure each pointer to gchar in new_cookie points to zero. This will make free_cookie() work better, because it will only free non-zero pointers */ static void initialise_cookie(gchar *new_cookie[COOKIE_COLUMNS]) { int i; for (i = 0; i < COOKIE_COLUMNS; i++) { new_cookie[i] = NULL; } } static void split_cookie_line(gchar *line, gchar *new_cookie[COOKIE_COLUMNS]) { /* from the string 'line', all the following ints will hold the start position and length of each cookie part */ gchar *line_ptr; /* pointer that will be incremented along a string */ size_t domain_start, domain_length; size_t suffix_start, suffix_length; size_t path_start, path_length; size_t secure_start, secure_length; size_t expire_start, expire_length; size_t name_start, name_length; size_t value_start, value_length; size_t tab_locations[6]; size_t number_of_tabs_found; size_t line_length; /* length of 'line' not including terminating '\0' */ size_t i; /* iterator */ time_t cookie_time; struct tm *cookie_tm_ptr; /* make line_ptr and line equal. Later, line_ptr will get incremented along the string that line points to */ line_ptr = line; line_length = strlen(line); number_of_tabs_found = 0; for (i = 0; i <= line_length; i++) { if (line[i] == '\t') { tab_locations[number_of_tabs_found] = i; number_of_tabs_found++; /* DON'T even think about trying to assign an extra tab past the bounds of the array! So if there are too many tabs, break out of the loop! */ if (number_of_tabs_found >= 6) { break; } } } /* enum COLUMN_NAMES {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE};*/ domain_start = 0; suffix_start = tab_locations[0] + 1; path_start = tab_locations[1] + 1; secure_start = tab_locations[2] + 1; expire_start = tab_locations[3] + 1; name_start = tab_locations[4] + 1; value_start = tab_locations[5] + 1; domain_length = suffix_start - domain_start - 1; suffix_length = path_start - suffix_start - 1; path_length = secure_start - path_start - 1; secure_length = expire_start - secure_start - 1; expire_length = name_start - expire_start - 1; name_length = value_start - name_start - 1; value_length = line_length - value_start - 1; new_cookie[COL_DOMAIN] = calloc(domain_length + 1, sizeof(gchar)); strncpy(new_cookie[COL_DOMAIN], line_ptr, domain_length); new_cookie[COL_DOMAIN][domain_length] = '\0'; /* increment line_ptr so that it points to a shorter line */ line_ptr += (domain_length + 1); new_cookie[COL_SUFFIX] = calloc(suffix_length + 1, sizeof(gchar)); strncpy(new_cookie[COL_SUFFIX], line_ptr, suffix_length); new_cookie[COL_SUFFIX][suffix_length] = '\0'; line_ptr += (suffix_length + 1); new_cookie[COL_PATH] = calloc(path_length + 1, sizeof(gchar)); strncpy(new_cookie[COL_PATH], line_ptr, path_length); new_cookie[COL_PATH][path_length] = '\0'; line_ptr += (path_length + 1); new_cookie[COL_SECURE] = calloc(secure_length + 1, sizeof(gchar)); strncpy(new_cookie[COL_SECURE], line_ptr, secure_length); new_cookie[COL_SECURE][secure_length] = '\0'; line_ptr += (secure_length + 1); new_cookie[COL_EXPIRE_SECONDS] = calloc(expire_length + 1, sizeof(gchar)); strncpy(new_cookie[COL_EXPIRE_SECONDS], line_ptr, expire_length); new_cookie[COL_EXPIRE_SECONDS][expire_length] = '\0'; line_ptr += (expire_length + 1); new_cookie[COL_NAME] = calloc(name_length + 1, sizeof(gchar)); strncpy(new_cookie[COL_NAME], line_ptr, name_length); new_cookie[COL_NAME][name_length] = '\0'; line_ptr += (name_length + 1); /* OK, this is a bit scary, but for some reason, we have to go one extra char here... otherwise, we lose the last char of the value! (which is the last char of the line, not counting the '\0') */ new_cookie[COL_VALUE] = calloc(value_length + 2, sizeof(gchar)); strncpy(new_cookie[COL_VALUE], line_ptr, value_length + 1); new_cookie[COL_VALUE][value_length + 1] = '\0'; /* a human-readable date is created and placed in slot 7 */ cookie_time = atoi(new_cookie[COL_EXPIRE_SECONDS]); /* cookie_tm_ptr = malloc(sizeof(struct tm)); seems unnecessary */ cookie_tm_ptr = gmtime(&cookie_time); /* interestingly, asctime() always returns a string 26 gchars long, including a '\n' and a '\0' at the end. I think I'll turn the '\n' into a ' ' */ new_cookie[COL_EXPIRE_DATE] = calloc(26, sizeof(gchar)); strncpy(new_cookie[COL_EXPIRE_DATE], asctime(cookie_tm_ptr), 26); /* change the '\n' at slot 24 to a ' ' */ new_cookie[COL_EXPIRE_DATE][24] = ' '; /* free(cookie_tm_ptr); causes SIGSEGV */ } static void free_cookie(gchar *c[COOKIE_COLUMNS]) { int i; for (i = 0; i < COOKIE_COLUMNS; i++) { if (c[i] != NULL) { free(c[i]); c[i] = NULL; } } } static void copy_cookie(gchar *c1[COOKIE_COLUMNS], gchar *c2[COOKIE_COLUMNS]) { /* COPIES c1 to c2 */ /* ASSUMPTION: c1 and c2's pointers are all initialised, either to NULL or to a string of gchar */ int i; size_t field_length; /* if c2 was pointing to anything, it won't be for long... */ /* Remember: free_cookie frees any memory pointed to by c2 as well as setting all of c2's pointers to null */ free_cookie(c2); for (i = 0; i < COOKIE_COLUMNS; i++) { if (c1[i] != NULL) { field_length = strlen(c1[i]); c2[i] = calloc(field_length + 1, sizeof(gchar)); strncpy(c2[i], c1[i], field_length); c2[i][field_length] = '\0'; } } } static void copy_clist_row_to_cookie(gint row, gchar *c[COOKIE_COLUMNS]) { int i; size_t field_length; gchar *cell_contents; /* if c was pointing to anything, it won't be for long... */ /* Remember: free_cookie frees any memory pointed to by c as well as setting all of c's pointers to null */ free_cookie(c); /* if the row is larger than the number of rows in the clist, we can return right now */ if (row >= glbl_clist_rows) { return; } for (i = 0; i < COOKIE_COLUMNS; i++) { gtk_clist_get_text(GTK_CLIST (glbl_clist), row, i, &cell_contents); if (cell_contents != NULL) { field_length = strlen(cell_contents); c[i] = calloc(field_length + 1, sizeof(gchar)); strncpy(c[i], cell_contents, field_length); c[i][field_length] = '\0'; } } } /* * GtkEntry */ static void show_edit_dialog_box (GtkWidget *calling_widget, gpointer func_data) { static GtkWidget *window = NULL; GtkWidget *box1; GtkWidget *box2; GtkWidget *table; GtkWidget *hbox; GtkWidget *text_entry[COOKIE_COLUMNS]; GtkWidget *suffix_tf_checkbox, *secure_tf_checkbox; GtkWidget *OK_button; GtkWidget *Cancel_button; GtkWidget *separator; GtkWidget *label, *exp_s_label; gint row; row = glbl_clist_selected_row; if (!window) { window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); /* any keypress over this window calls handle_keypress_in_edit_win so that when an or is detected, it calls what the OK and Cancel buttons would do respectively */ gtk_signal_connect(GTK_OBJECT(window), "key_press_event", GTK_SIGNAL_FUNC(handle_keypress_in_edit_win), NULL); gtk_window_set_title (GTK_WINDOW (window), "Edit Cookie"); gtk_container_border_width (GTK_CONTAINER (window), 0); box1 = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), box1); gtk_widget_show (box1); box2 = gtk_vbox_new (FALSE, 10); gtk_container_border_width (GTK_CONTAINER (box2), 10); gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); gtk_widget_show (box2); table = gtk_table_new(9, 2, FALSE); gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* COL_DOMAIN Name Edit Box */ label = gtk_label_new(glbl_cookie_titles_str[COL_DOMAIN]); text_entry[COL_DOMAIN] = gtk_entry_new (); /* I need to pass a pointer to an int as an argument for the signal connect function. The int represents the column of the bit of cookie data. That's what the glbl_int_ptr[COL_DOMAIN] is for. */ gtk_signal_connect(GTK_OBJECT(text_entry[COL_DOMAIN]), "changed", GTK_SIGNAL_FUNC(change_callback), glbl_int_ptr[COL_DOMAIN]); gtk_entry_set_text (GTK_ENTRY (text_entry[COL_DOMAIN]), glbl_selected_cookie[COL_DOMAIN]); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); gtk_table_attach_defaults(GTK_TABLE(table), text_entry[COL_DOMAIN], 1, 2, 0, 1); gtk_widget_show (label); gtk_widget_show (text_entry[COL_DOMAIN]); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* Name Edit Box */ label = gtk_label_new(glbl_cookie_titles_str[COL_NAME]); text_entry[COL_NAME] = gtk_entry_new (); gtk_signal_connect(GTK_OBJECT(text_entry[COL_NAME]), "changed", GTK_SIGNAL_FUNC(change_callback), glbl_int_ptr[COL_NAME]); gtk_entry_set_text (GTK_ENTRY (text_entry[COL_NAME]), glbl_selected_cookie[COL_NAME]); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); gtk_table_attach_defaults(GTK_TABLE(table), text_entry[COL_NAME], 1, 2, 1, 2); gtk_widget_show (label); gtk_widget_show (text_entry[COL_NAME]); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* Value Edit Box */ label = gtk_label_new(glbl_cookie_titles_str[COL_VALUE]); text_entry[COL_VALUE] = gtk_entry_new (); gtk_signal_connect(GTK_OBJECT(text_entry[COL_VALUE]), "changed", GTK_SIGNAL_FUNC(change_callback), glbl_int_ptr[COL_VALUE]); gtk_entry_set_text (GTK_ENTRY (text_entry[COL_VALUE]), glbl_selected_cookie[COL_VALUE]); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); gtk_table_attach_defaults(GTK_TABLE(table), text_entry[COL_VALUE], 1, 2, 2, 3); gtk_widget_show (label); gtk_widget_show (text_entry[COL_VALUE]); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* COL_PATH Edit Box */ label = gtk_label_new(glbl_cookie_titles_str[COL_PATH]); text_entry[COL_PATH] = gtk_entry_new (); gtk_signal_connect(GTK_OBJECT(text_entry[COL_PATH]), "changed", GTK_SIGNAL_FUNC(change_callback), glbl_int_ptr[COL_PATH]); gtk_entry_set_text (GTK_ENTRY (text_entry[COL_PATH]), glbl_selected_cookie[COL_PATH]); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); gtk_table_attach_defaults(GTK_TABLE(table), text_entry[COL_PATH], 1, 2, 3, 4); gtk_widget_show (label); gtk_widget_show (text_entry[COL_PATH]); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* COL_SUFFIX T/F Edit Box */ suffix_tf_checkbox = gtk_check_button_new_with_label ("Domain field is actually a suffix"); gtk_signal_connect(GTK_OBJECT(suffix_tf_checkbox), "clicked", GTK_SIGNAL_FUNC(change_tf_callback), glbl_int_ptr[COL_SUFFIX]); /* turn the button on or off depending on whether the cookie field is "TRUE" or "FALSE" */ if (strcmp(glbl_selected_cookie[COL_SUFFIX], "TRUE") == 0) { /* checkbox is checked */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(suffix_tf_checkbox), TRUE ); } else { /* checkbox is unchecked */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(suffix_tf_checkbox), FALSE ); } gtk_table_attach_defaults(GTK_TABLE(table), suffix_tf_checkbox, 0, 2, 4, 5); gtk_widget_show (suffix_tf_checkbox); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* COL_SECURE T/F Edit Box */ secure_tf_checkbox = gtk_check_button_new_with_label ("Make this cookie secure"); gtk_signal_connect(GTK_OBJECT(secure_tf_checkbox), "clicked", GTK_SIGNAL_FUNC(change_tf_callback), glbl_int_ptr[COL_SECURE]); /* turn the button on or off depending on whether the cookie field is "TRUE" or "FALSE" */ if (strcmp(glbl_selected_cookie[COL_SECURE], "TRUE") == 0) { /* checkbox is checked */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(secure_tf_checkbox), TRUE ); } else { /* checkbox is unchecked */ gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(secure_tf_checkbox), FALSE ); } gtk_table_attach_defaults(GTK_TABLE(table), secure_tf_checkbox, 0, 2, 5, 6); gtk_widget_show (secure_tf_checkbox); /* Descriptive label above the human-readable date dialogue box */ label = gtk_label_new("\nCookie expiration dates are stored as\nthe number of seconds since 1 Jan 1970.\nAs you edit the human-readble date below,\nstick to the following format:\nWdy Mon dd hh:mm:ss yyyy and\ngtkcookie will be able to figure\nout the time in seconds for you.\n"); gtk_widget_show (label); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 6, 7); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* Expiry Date (in seconds) Label */ label = gtk_label_new(glbl_cookie_titles_str[COL_EXPIRE_SECONDS]); exp_s_label = gtk_label_new(glbl_selected_cookie[COL_EXPIRE_SECONDS]); /* have our global GtkWidget pointer point to the expiry date label, so that we can change the label even after this function has run */ glbl_selected_etl = exp_s_label; gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 7, 8); gtk_table_attach_defaults(GTK_TABLE(table), exp_s_label, 1, 2, 7, 8); gtk_widget_show (label); gtk_widget_show (exp_s_label); /* {COL_DOMAIN, COL_SUFFIX, COL_PATH, COL_SECURE, COL_EXPIRE_SECONDS, COL_NAME, COL_VALUE, COL_EXPIRE_DATE}; */ /* COL_EXPIRE_DATE Edit Box */ label = gtk_label_new(glbl_cookie_titles_str[COL_EXPIRE_DATE]); text_entry[COL_EXPIRE_DATE] = gtk_entry_new (); /* have to find a way to pass a pointer to the expiry date in seconds label to this function, so that the function can change the label. In the function itself, use the Linux-specific call strptime() to take care of parsing the date as it is edited. */ gtk_signal_connect(GTK_OBJECT(text_entry[COL_EXPIRE_DATE]), "changed", GTK_SIGNAL_FUNC(change_time_callback), glbl_int_ptr[COL_EXPIRE_DATE]); gtk_entry_set_text (GTK_ENTRY (text_entry[COL_EXPIRE_DATE]), glbl_selected_cookie[COL_EXPIRE_DATE]); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 8, 9); gtk_table_attach_defaults(GTK_TABLE(table), text_entry[COL_EXPIRE_DATE], 1, 2, 8, 9); gtk_widget_show (label); gtk_widget_show (text_entry[COL_EXPIRE_DATE]); gtk_widget_show (table); /* if we're done with the cookie here, nuke it and see what happens */ /* free_cookie(glbl_selected_cookie); */ /* OK, what happens is that the cookie really does contain pointers right into the glbl_clist's elements. Freeing the memory that the cookie's gchar pointers point to fucks up the elements in the glbl_clist */ /* To the best of my knowlege, gtk_entry_set_text copies the string from the pointer into the text widget, but de-allocation of the text seems to be the responsibility of the widget */ separator = gtk_hseparator_new (); gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); gtk_widget_show (separator); /* buttons at bottom */ box2 = gtk_vbox_new (FALSE, 10); gtk_container_border_width (GTK_CONTAINER (box2), 10); gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); gtk_widget_show (box2); hbox = gtk_hbox_new (TRUE, 10); OK_button = gtk_button_new_with_label ("OK"); /* note that I pass a pointer to this window as the second arg of this function, because update_glbl_clist() destroys the widget in arg2. This is useful, because in this case, I want the edit window to be destroyed after the OK button is clicked. */ gtk_signal_connect (GTK_OBJECT (OK_button), "clicked", GTK_SIGNAL_FUNC(update_glbl_clist), GTK_OBJECT(window)); gtk_box_pack_start (GTK_BOX (hbox), OK_button, TRUE, TRUE, 0); GTK_WIDGET_SET_FLAGS (OK_button, GTK_CAN_DEFAULT); gtk_widget_grab_default (OK_button); gtk_widget_show (OK_button); gtk_widget_show (hbox); Cancel_button = gtk_button_new_with_label ("Cancel"); gtk_signal_connect_object (GTK_OBJECT (Cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window)); gtk_box_pack_start (GTK_BOX (hbox), Cancel_button, TRUE, TRUE, 0); GTK_WIDGET_SET_FLAGS (Cancel_button, GTK_CAN_DEFAULT); gtk_widget_show (Cancel_button); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (box2), hbox, TRUE, TRUE, 0); } if (!GTK_WIDGET_VISIBLE (window)) { gtk_widget_show (window); } else { gtk_widget_destroy (window); } } static void change_callback(GtkWidget *entry, gpointer func_data) { gchar *entry_text; int i; /* the number of the cookie column */ size_t field_length; i = *(int *) func_data; entry_text = gtk_entry_get_text(GTK_ENTRY(entry)); if (entry_text != NULL) { field_length = strlen(entry_text); /* shouldn't I free the memory before re-allocating it? YES! */ if (glbl_selected_cookie[i] != NULL) { free(glbl_selected_cookie[i]); } glbl_selected_cookie[i] = calloc(field_length + 1, sizeof(gchar)); strncpy(glbl_selected_cookie[i], entry_text, field_length); glbl_selected_cookie[i][field_length] = '\0'; } } static void change_time_callback(GtkWidget *entry, gpointer func_data) { gchar *entry_text; int i; /* the number of the cookie column */ size_t field_length; i = *(int *) func_data; entry_text = gtk_entry_get_text(GTK_ENTRY(entry)); if (entry_text != NULL) { field_length = strlen(entry_text); /* shouldn't I free the memory before re-allocating it? YES! */ if (glbl_selected_cookie[i] != NULL) { free(glbl_selected_cookie[i]); } glbl_selected_cookie[i] = calloc(field_length + 1, sizeof(gchar)); strncpy(glbl_selected_cookie[i], entry_text, field_length); glbl_selected_cookie[i][field_length] = '\0'; strptime(glbl_selected_cookie[i], "%a %b %d %T %Y", &glbl_selected_time); glbl_selected_time_sec = mktime(&glbl_selected_time); sprintf(glbl_selected_time_str, "%i", (int)glbl_selected_time_sec); gtk_label_set( GTK_LABEL(glbl_selected_etl), glbl_selected_time_str); /* gtk_label_set( GTK_LABEL(glbl_selected_etl), glbl_selected_cookie[4]); */ /* remember, below, that glbl_selected_cookie[4] is the date in seconds */ if (glbl_selected_time_str != NULL) { field_length = strlen(glbl_selected_time_str); /* shouldn't I free the memory before re-allocating it? YES! */ if (glbl_selected_cookie[4] != NULL) { free(glbl_selected_cookie[4]); } glbl_selected_cookie[4] = calloc(field_length + 1, sizeof(gchar)); strncpy(glbl_selected_cookie[4], glbl_selected_time_str, field_length); glbl_selected_cookie[4][field_length] = '\0'; } } } static void change_tf_callback(GtkWidget *toggle_button, gpointer func_data) { int i; /* the number of the cookie column */ size_t field_length; gchar *True = "TRUE"; gchar *False = "FALSE"; gchar *Bool = NULL; i = *(int *) func_data; if (GTK_TOGGLE_BUTTON (toggle_button)->active) { Bool = True; } else { Bool = False; } field_length = strlen(Bool); /* shouldn't I free the memory before re-allocating it? YES! */ if (glbl_selected_cookie[i] != NULL) { free(glbl_selected_cookie[i]); } glbl_selected_cookie[i] = calloc(field_length + 1, sizeof(gchar)); strncpy(glbl_selected_cookie[i], Bool, field_length); glbl_selected_cookie[i][field_length] = '\0'; } /* static void update_clist(GtkWidget *clist, gpointer window) { */ static void update_glbl_clist(GtkWidget *calling_widget, GtkWidget *edit_win_to_destroy) { int column; for (column = 0; column < COOKIE_COLUMNS; column++) { gtk_clist_set_text(GTK_CLIST(glbl_clist), glbl_clist_selected_row, column, glbl_selected_cookie[column] ); } glbl_last_sorted_column = -1; /* because we just set the text in a column somewhere in the clist, no columns can be assumed to be sorted anymore */ gtk_widget_destroy(edit_win_to_destroy); } /* FILE SELECTION STUFF */ static void file_selection_ok (GtkWidget *w, GtkFileSelection *fs) { size_t length; gchar *filename; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)); if (glbl_selected_filename_str != NULL) { free(glbl_selected_filename_str); } if (filename != NULL) { length = strlen(filename); glbl_selected_filename_str = calloc(length + 1, sizeof(gchar)); strncpy(glbl_selected_filename_str, filename, length); glbl_selected_filename_str[length] = '\0'; } gtk_widget_destroy (GTK_WIDGET (fs)); /* clear the glbl_clist */ clear_glbl_clist(); populate_glbl_clist(); } static void show_open_file_dialog_box (GtkWidget *calling_widget, gpointer func_data) { static GtkWidget *window = NULL; if (!window) { window = gtk_file_selection_new ("Open"); gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (window)); gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button), "clicked", GTK_SIGNAL_FUNC(file_selection_ok), window); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window)); } if (!GTK_WIDGET_VISIBLE (window)) { gtk_widget_show (window); } else { gtk_widget_destroy (window); } } static void show_save_as_dialog_box (GtkWidget *calling_widget, gpointer func_data) { static GtkWidget *window = NULL; if (!window) { window = gtk_file_selection_new ("Save As"); gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (window)); gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button), "clicked", GTK_SIGNAL_FUNC(save_as_ok), window); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window)); if (glbl_selected_filename_str != NULL) { gtk_file_selection_set_filename ( GTK_FILE_SELECTION(window), glbl_selected_filename_str); } } if (!GTK_WIDGET_VISIBLE (window)) { gtk_widget_show (window); } else { gtk_widget_destroy (window); } } static void save_as_ok (GtkWidget *w, GtkFileSelection *fs) { size_t length; gchar *filename; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)); if (glbl_selected_filename_str != NULL) { free(glbl_selected_filename_str); /* don't I set to null as well? 55555 */ } if (filename != NULL) { length = strlen(filename); glbl_selected_filename_str = calloc(length + 1, sizeof(gchar)); strncpy(glbl_selected_filename_str, filename, length); glbl_selected_filename_str[length] = '\0'; } gtk_widget_destroy (GTK_WIDGET (fs)); /* set the title of the main window to show the selected filename */ /* first, erase what's currently in the gstring */ g_string_erase(glbl_window_title_gstr, 0, glbl_window_title_gstr->len); /* now create the new title, always starting with "gtkcookie - " and then the filename */ g_string_assign(glbl_window_title_gstr, "gtkcookie - "); g_string_append(glbl_window_title_gstr, glbl_selected_filename_str); /* set the title of the main window */ gtk_window_set_title(GTK_WINDOW (glbl_main_window), glbl_window_title_gstr->str); /* now that glbl_selected_filename_str has got the new value, call save_file(), which will save glbl_selected_filename_str */ /* note too that save_file is a callback function, which means that it can be passed a pointer to the calling widget and to extra function data, neither of which we need here; hence, the two NULLs */ save_file(NULL, NULL); } static void check_for_running_netscape(void) { uid_t uid_of_this_process; gboolean error; /* was there an error trying to find a process named "netscape"? */ pid_t netscape_pid = 0; GString *error_msg_gstr; uid_of_this_process = getuid(); /* if a "netscape" is running under the same uid of this programme, then we know the user is running netscape */ netscape_pid = process_is_running("netscape", uid_of_this_process, &error); /* note that Red Had actually runs netscape as "netscape-commun", so check for that too, if the first check revealed nothing */ if (netscape_pid == 0) { netscape_pid = process_is_running("netscape", uid_of_this_process, &error); } if (netscape_pid != 0) { error_msg_gstr = g_string_new(""); g_string_sprintf(error_msg_gstr, "It looks like you're running Netscape at pid %i right now.\nPlease note that your changes to your netscape cookie file\nwill be lost if you leave Netscape running.\nNetscape writes the cookie file just before exiting,\nand could therefore clobber any changes you make to the file now.", netscape_pid); show_error_dialog_box(error_msg_gstr->str); /* don't forget to deallocate the string after we're done with it! Fortunately, deallocating the string seems not to harm the error message created by show_error_dialog_box() */ g_string_free(error_msg_gstr, TRUE); } } static void search (GtkWidget* wdg, gpointer data) { gint row; gint column; gchar *needle, *cell_contents; /* time to try to use gstrings, methinks... */ /* oooh, gstrings are sexy! */ GString *cell_str; /* the string from a cell in the clist */ GString *find_str; /* the string in the "find" text entry box */ needle = (gchar*) gtk_entry_get_text (GTK_ENTRY (data)); if (!strlen (needle)) { return; } find_str = g_string_new (needle); /* if the search is case-insensitive, make everything in find_str uppercase */ if (glbl_search_case_sensitive == FALSE) { g_string_up(find_str); } for (row = glbl_clist_selected_row; row < glbl_clist_rows; row++) { for (column = 0; column < COOKIE_COLUMNS; column++) { gtk_clist_get_text(GTK_CLIST (glbl_clist), row, column, &cell_contents); /* we need to copy construct cell_str, so that when we uppercase cell_str, we are not uppercasing the actual clist cell data that cell_str points to! */ cell_str = g_string_new(""); g_string_append(cell_str, cell_contents); /* if the search is case-insensitive, make everything in cell_str uppercase */ if (glbl_search_case_sensitive == FALSE) { g_string_up(cell_str); } if (strstr(cell_str->str, find_str->str) != NULL && row > glbl_last_found_row) { /* in the if statement directly above, we don't count a search that is successful in the last found row, because that search has already been done, and the user is interested in rows AFTER the current row. In the case of a match after the last found row, set the last found row to the currently matched row so that the number is accurate for the next time this function is called */ glbl_last_found_row = row; gtk_clist_unselect_row(GTK_CLIST(glbl_clist), row, column); /* manni: perhaps if you don't check for visibility, but simply always do the moveto, you will conquer the bug where a row is sometimes just off the display area (due to the bottom scrollbar?) */ if (gtk_clist_row_is_visible(GTK_CLIST(glbl_clist), row) == GTK_VISIBILITY_NONE) { gtk_clist_moveto(GTK_CLIST(glbl_clist), row, column, 0.5, 0.5); } gtk_clist_select_row(GTK_CLIST(glbl_clist), row, column); select_clist(glbl_clist, row, column, NULL); /* DEALLOCATE! */ g_string_free(cell_str, TRUE); g_string_free(find_str, TRUE); return; } /* DEALLOCATE! */ g_string_free(cell_str, TRUE); } } /* DEALLOCATE! */ g_string_free(find_str, TRUE); /* if we made it this far, we must have a not found condition! */ show_error_dialog_box("Not found."); } static void clear_search (GtkWidget* wdg, gpointer data) { gtk_entry_set_text (GTK_ENTRY (data), ""); /* the last found row is rendered invalid when the user clears the text box, so reset that variable to -1 */ glbl_last_found_row = -1; } static void reset_last_found_row(GtkWidget *calling_widget, gpointer func_data) { glbl_last_found_row = -1; } /* seems not to be working static void set_cursor (guint c) { GdkCursor *cursor; c = CLAMP (c, 0, 152); c &= 0xfe; cursor = gdk_cursor_new (c); gdk_window_set_cursor (glbl_main_window->window, cursor); gdk_cursor_destroy (cursor); } */ static void quicksort_clist(GtkWidget *clist, SORT_STYLE sort_style, gint sort_column, gint first_index, gint last_index) { /* clist is the clist */ /* sort column is the column that we are sorting on */ /* first_index is the index of the first element of the (sub)array */ /* n is the number of elements in the clist */ gint pivot_index; /* index of the pivot element */ if (first_index < last_index) { /* partition the array and set the pivot_index */ partition_clist(clist, sort_style, sort_column, first_index, &pivot_index, last_index); /* recursive calls will now sort the subarrays */ quicksort_clist(clist, sort_style, sort_column, first_index, (pivot_index - 1)); quicksort_clist(clist, sort_style, sort_column, (pivot_index + 1), last_index); } } static void partition_clist(GtkWidget *clist, SORT_STYLE sort_style, gint sort_column, gint first_index, gint *pivot_index, gint last_index) { gchar *pivot_cookie[COOKIE_COLUMNS]; gchar *sort_column_cell_contents; /* text inside the cell to be sorted */ gchar *cell_contents; /* generic contents of any cell */ gchar *more_cell_contents; /* generic contents of any cell */ gint col; /* column */ int (*compfunction)(const char*, const char*); /* pointer to a comparison function that takes two strings as its args */ gint too_big_index = first_index + 1; gint too_small_index = last_index; /* hopefully this makes the code more readable, not less -- besides which, last_index isn't allowed to change, whereas too_small_index will be changing a lot */ initialise_cookie(pivot_cookie); /* always do this with a new cookie! */ /* choose the first cookie as the pivot_cookie */ copy_clist_row_to_cookie(first_index, pivot_cookie); /* make the comparison function either strcmp from the standard C library, or intcmp, defined by me in this code. */ if (sort_style == STRING) { compfunction = strcmp; } else { compfunction = intcmp; } while (too_big_index <= too_small_index) { gtk_clist_get_text(GTK_CLIST (clist), too_big_index, sort_column, &sort_column_cell_contents); /* with strcmp, instead use a pointer to some function depending on the sort column, that way we can have the function return true or false depending on whatever comparison is necessary for the sort column. This way, we can do a strcmp for strings, but a atol on both strings, and a numeric comparison for dates */ /* SORT: strcmp <= 0 */ /* while (too_big_index <= last_index && strcmp(sort_column_cell_contents, pivot_cookie[sort_column]) <= 0) { */ while (too_big_index <= last_index && (*compfunction)(sort_column_cell_contents, pivot_cookie[sort_column]) <= 0) { ++too_big_index; gtk_clist_get_text(GTK_CLIST (clist), too_big_index, sort_column, &sort_column_cell_contents); } gtk_clist_get_text(GTK_CLIST (clist), too_small_index, sort_column, &sort_column_cell_contents); /* SORT: strcmp > 0 */ /* while (strcmp(sort_column_cell_contents, pivot_cookie[sort_column]) > 0) { */ while ((*compfunction)(sort_column_cell_contents, pivot_cookie[sort_column]) > 0) { --too_small_index; gtk_clist_get_text(GTK_CLIST (clist), too_small_index, sort_column, &sort_column_cell_contents); } if (too_big_index < too_small_index) { swap_row_clist(clist, too_big_index, too_small_index); gtk_clist_get_text(GTK_CLIST (clist), too_big_index, sort_column, &cell_contents); gtk_clist_get_text(GTK_CLIST (clist), too_small_index, sort_column, &more_cell_contents); } } *pivot_index = too_small_index; /* move the data at the pivot_index (which still contains the value that is less than or equal to the pivot) to the first slot. Unless, of course, first_index and *pivot_index are already pointing to the same slot. x*/ if (first_index != *pivot_index) { for (col = 0; col < COOKIE_COLUMNS; col++) { gtk_clist_get_text(GTK_CLIST (clist), *pivot_index, col, &cell_contents); gtk_clist_set_text(GTK_CLIST (clist), first_index, col, cell_contents); } } /* move what's in the pivot_cookie to the *pivot_index in the clist */ for (col = 0; col < COOKIE_COLUMNS; col++) { gtk_clist_set_text(GTK_CLIST (clist), *pivot_index, col, pivot_cookie[col]); } /* DEALLOCATE pivot_cookie */ free_cookie(pivot_cookie); } /* set case sensitive searching TRUE/FALSE */ /* (held in the global variable glbl_search_case_sensitive) */ static void set_tf_case_sensitive_search(GtkWidget *toggle_button, gpointer func_data) { if (GTK_TOGGLE_BUTTON (toggle_button)->active) { glbl_search_case_sensitive = TRUE; } else { glbl_search_case_sensitive = FALSE; } } static gint search_dlg_key_cb (GtkWidget* wdg, GdkEventKey* ev, gpointer data) { if (ev->keyval == GDK_Return) { search (wdg, data); return TRUE; } else if (ev->keyval == GDK_Escape) { gtk_widget_destroy (wdg); return TRUE; /* tell calling code that we have handled this event */ } return FALSE; } gint handle_keypress_in_edit_win(GtkWidget *edit_window, GdkEventKey *event, gpointer func_data) { if (event->keyval == GDK_Return) { /* note how the first arg below is NULL. update_glbl_clist often gets called as a callback function, so a pointer to the calling widget is generally passed as the first argument. But here, I'm calling it manually, so I just leave the first arg NULL. Doesn't seem to break anything, either. */ update_glbl_clist(NULL, edit_window); return TRUE; /* tell calling code that we have handled this event */ } if (event->keyval == GDK_Escape) { gtk_widget_destroy(edit_window); return TRUE; /* tell calling code that we have handled this event */ } return FALSE; } gint handle_keypress_in_main_win(GtkWidget *calling_widget, GdkEventKey *event, gpointer func_data) { if (event->keyval == GDK_Delete) { /* note arg1 below is null, because with callback functions, arg1 is automatically populated with a pointer to the calling widget; arg2 is null because it's a pointer to any function data you may need to pass to the function... which we don't */ remove_selected_row_from_glbl_clist(NULL, NULL); return TRUE; /* tell calling code that we have handled this event */ } return FALSE; } /* end */ gtkcookie-0.4/proctool.c0000644000175000017500000001675306624707100016771 0ustar mechanixmechanix00000000000000/* proctool.c --> tool for finding processes on the system Copyright (c) 1998 Manni Wood This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Manni Wood: mwood@sig.bsh.com, pq1036@110.net */ #include #include #include #include #include #include /* defines pid_t, etc */ #include #include #include /* contains all keyboard defs */ #include /* isdigit() needed by string_contains_all_numbers */ #include "proctool.h" #include "getline.h" static gboolean string_contains_all_numbers(const gchar *str); static gboolean statusfile_contains(const gchar *filename, const gchar *procname, uid_t user_id); static gboolean nameline_matches_string(const gchar *file_line, const gchar *procname); static gboolean uidline_matches_string(const gchar *file_line, uid_t user_id); static gboolean line_is_field(const gchar *file_line, const gchar *fieldname); pid_t process_is_running(const gchar *procname, uid_t user_id, gboolean *checkerrno) { DIR *dir; struct dirent *ent; pid_t netscape_pid = 0; /* ensure this is a default value of 0, so that if it isn't assigned to, it will at least have the "failure" value */ GString *statusfilename; *checkerrno = FALSE; /* default: no errors encountered */ if (!(dir = opendir("/proc"))) { *checkerrno = TRUE; return 0; } /* reset errno to 0, so we can tell when readdir() fails */ errno = 0; while ((ent = readdir(dir))) { /* each directory inside /proc that is a number, is a process number directory */ if (string_contains_all_numbers(ent->d_name)) { statusfilename = g_string_new("/proc/"); g_string_append(statusfilename, ent->d_name); g_string_append(statusfilename, "/status"); /* statusfilename should now equal something like /proc/89/status, which is the filename of a text document holding status info on pid 89 */ if (statusfile_contains(statusfilename->str, procname, user_id)) { /* turn the directory name into an int */ netscape_pid = atoi(ent->d_name); } g_string_free(statusfilename, TRUE); } } if (errno) { *checkerrno = TRUE; return 0; } closedir(dir); return netscape_pid; } static gboolean string_contains_all_numbers(const gchar *str) { size_t i; i = 0; while (str[i] != '\0') { if (!(isdigit(str[i]))) { return FALSE; } ++i; } return TRUE; } static gboolean statusfile_contains(const gchar *filename, const gchar *procname, uid_t user_id) { /* so here, we have to open up the file, read it in, and look for a line that starts with Name:\tnetscape, and another line that starts with Uid:\t1277\t1277\t1277\t1277. Do a string match on the Name:, and turn the Uid: into an int and do a numeric equality comparison. If both are true, return TRUE, else return FALSE. */ FILE *statfile = NULL; /* were we successful reading a line from the file? */ gboolean success = FALSE; /* line of text from a file */ gchar *file_line = NULL; /* did the process name match *procname? */ gboolean match_procname = FALSE; /* did the Uid: match user_id? */ gboolean match_uid = FALSE; statfile = fopen(filename, "r"); if (statfile == NULL) { /* if we couldn't open the status file, let's assume for now that the string isn't in the file */ printf("cannot read file \"%s\"\n", filename); /* BUT REALLY, WE WILL NEED MUCH NICER ERROR MESSAGE HANDLING AND PASSING HERE */ return FALSE; } file_line = get_line_safely(statfile, &success); if (success) { if (line_is_field(file_line, "Name:") && nameline_matches_string(file_line, procname)) { match_procname = TRUE; } if (line_is_field(file_line, "Uid:") && uidline_matches_string(file_line, user_id)) { match_uid = TRUE; } } free(file_line); file_line = NULL; while (success) { file_line = get_line_safely(statfile, &success); if (success) { if (line_is_field(file_line, "Name:") && nameline_matches_string(file_line, procname)) { match_procname = TRUE; } if (line_is_field(file_line, "Uid:") && uidline_matches_string(file_line, user_id)) { match_uid = TRUE; } } free(file_line); file_line = NULL; } fclose(statfile); return match_procname && match_uid; } static gboolean nameline_matches_string(const gchar *file_line, const gchar *procname) { /* a name line is of the format: Name:\tprocname\0 */ /* here's a cute bit o' tricky code: I want to start comparing file_line from the 6th character, because "Name:\t" is 6 chars before the first char of the actual process name. So, I add 6 to the pointer file_line, and suddenly the pointer is pointing to the first char of the process name. Hence, we can accurately compare file_line + 6 with procname */ if (strcmp(file_line + 6, procname) == 0) { return TRUE; } else { return FALSE; } } static gboolean uidline_matches_string(const gchar *file_line, uid_t user_id) { /* a uidline reads: Uid:\t\d\t\d\t\d\t\d\0 */ gchar *uidstr; uid_t this_uid; size_t i; size_t uid_offset = 5; /* the offset to the first char of uid */ size_t number_of_tabs_found = 0; size_t line_length; /* length of file_line */ size_t tab_locations[4]; /* there are 4 tabs in the uidline, so let's track where each one is */ line_length = strlen(file_line); for (i = 0; i <= line_length; i++) { if (file_line[i] == '\t') { tab_locations[number_of_tabs_found] = i; ++number_of_tabs_found; /* DON'T even think about trying to assign an extra tab past the bounds of the array! So if there are too many tabs, break out of the loop! */ if (number_of_tabs_found >= 4) { break; } } } /* if there aren't 4 tabs, this isn't a valid line! */ if (number_of_tabs_found != 4) { return FALSE; } /* get length and malloc! */ uidstr = calloc(tab_locations[1] - uid_offset + 1, sizeof(gchar)); uidstr = strncpy(uidstr, file_line + uid_offset, tab_locations[1] - uid_offset); /* don't forget the final \0 */ uidstr[tab_locations[1] - uid_offset] = '\0'; this_uid = atoi(uidstr); /* DEALLOCATE! */ free(uidstr); if (this_uid == user_id) { return TRUE; } else { return FALSE; } } static gboolean line_is_field(const gchar *file_line, const gchar *fieldname) { size_t length; length = strlen(fieldname); /* in other words, find out how long the field is, and only compare that many characters of file_line to fieldname */ if (strncmp(file_line, fieldname, length) == 0) { return TRUE; } else { return FALSE; } } gtkcookie-0.4/proctool.h0000644000175000017500000000376406624707100016774 0ustar mechanixmechanix00000000000000#ifndef PROCTOOL_H #define PROCTOOL_H /* proctool.h --> tool for finding processes on the system Copyright (c) 1998 Manni Wood This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Manni Wood: mwood@sig.bsh.com, pq1036@110.net */ #include #include #include #include #include #include /* defines pid_t, etc */ #include /* will return the pid of the running process named in procname, or 0 if the process wasn't found. If an error message occurred, checkerrno will be TRUE, else FALSE. In the event checkerrno is TRUE, strerror(errno) can be used to find the exact nature of the error. (strerror(errno) is a part of the standard unix libary, and errno is a global variable) */ /* notes: a pid_t is a positive integer that uniquely identifies a running process a uid_t is a positive integer holding a user id */ pid_t process_is_running(const gchar *procname, uid_t user_id, gboolean *checkerrno); #endif /* ifndef PROCTOOL_H */ /* sprintf(error_msg_gstr, "Can't open /proc directory.\n\"%s\"", strerror(errno)); if (errno) { error_msg_gstr = g_string_new(""); g_string_sprintf(error_msg_gstr, "Error reading the proc directory.\n\"%s\"", strerror(errno)); show_error_dialog_box(error_msg_gstr->str); g_string_free(error_msg_gstr, TRUE); return; } */ gtkcookie-0.4/small_cookie.xpm0000644000175000017500000000102106624707100020131 0ustar mechanixmechanix00000000000000/* XPM */ static char * small_cookie_xpm[] = { "16 16 10 1", " c None", ". c #C0C0C0", "+ c #CC9966", "@ c #996633", "# c #996600", "$ c #993300", "% c #666600", "& c #663333", "* c #663300", "= c #330000", " ", " +@###@+ ", " @###$%$#@ ", " @###=%*$%#@ ", " +##**=$*%$##+ ", " @#*#*%*$**=#@ ", " ###=*$%*=#*## ", " ##***%*$**#*# ", " #*#*#$#%$**## ", " +@=%*$%=#*# ", " &$%$%$%$+ ", " +*=*#*#@ ", " %**##@ ", " $##@+ ", " ", " "}; gtkcookie-0.4/AUTHORS0000644000175000017500000000016307543705664016036 0ustar mechanixmechanix00000000000000gtkcookie - originally developed by Manni Wood Currently maintained by Filip Van Raemdonck gtkcookie-0.4/gtkcookie.desktop0000644000175000017500000000031110117103364020307 0ustar mechanixmechanix00000000000000[Desktop Entry] Version=1.0 Type=Application Encoding=UTF-8 Name=GtkCookie Comment=Edit browser cookies Exec=gtkcookie Icon=/usr/share/pixmaps/cookie.xpm Categories=Application;Network; Terminal=false