├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── config.h ├── config.mk ├── drw.c ├── drw.h ├── pinentry-dmenu.1 ├── pinentry-dmenu.c ├── pinentry ├── AUTHORS ├── COPYING ├── Makefile ├── argparse.c ├── argparse.h ├── memory.h ├── password-cache.c ├── password-cache.h ├── pinentry.c ├── pinentry.h ├── secmem++.h ├── secmem-util.h ├── secmem.c ├── util.c └── util.h ├── test ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.o 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 675 Mass Ave, Cambridge, MA 02139, USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # pinentry-dmenu - dmenu-like stupid pin entry 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = pinentry-dmenu.c drw.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | OBJ_PIN = pinentry/pinentry.o pinentry/util.o pinentry/password-cache.o pinentry/argparse.o pinentry/secmem.o 9 | 10 | all: options pinentry-dmenu 11 | 12 | options: 13 | @echo pinentry-dmenu build options: 14 | @echo "CFLAGS = ${CFLAGS}" 15 | @echo "LDFLAGS = ${LDFLAGS}" 16 | @echo "CC = ${CC}" 17 | 18 | .c.o: 19 | @echo CC $< 20 | @${CC} -c ${CFLAGS} $< 21 | 22 | config.h: 23 | @echo creating $@ from config.def.h 24 | @cp config.def.h $@ 25 | 26 | ${OBJ}: config.h config.mk drw.h 27 | 28 | pinentry: 29 | $(MAKE) -C pinentry 30 | 31 | pinentry-dmenu: pinentry pinentry-dmenu.o drw.o util.o 32 | @echo CC -o $@ 33 | @${CC} -o $@ ${OBJ} ${OBJ_PIN} ${LDFLAGS} -lassuan -lgpgme -lgpg-error -lconfig 34 | 35 | clean: 36 | @echo cleaning 37 | @rm -f pinentry-dmenu ${OBJ} 38 | $(MAKE) -C pinentry/ clean 39 | 40 | dist: clean 41 | @echo creating dist tarball 42 | @mkdir -p dmenu-${VERSION} 43 | @cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1 \ 44 | drw.h util.h dmenu_path dmenu_run stest.1 ${SRC} \ 45 | dmenu-${VERSION} 46 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} 47 | @gzip dmenu-${VERSION}.tar 48 | @rm -rf dmenu-${VERSION} 49 | 50 | install: all 51 | @echo installing executable to ${DESTDIR}${PREFIX}/bin 52 | @mkdir -p ${DESTDIR}${PREFIX}/bin 53 | @cp -f pinentry-dmenu ${DESTDIR}${PREFIX}/bin 54 | @chmod 755 ${DESTDIR}${PREFIX}/bin/pinentry-dmenu 55 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 56 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 57 | @sed "s/VERSION/${VERSION}/g;s/DATE/${DATE}/g;s/BUGREPORT/${BUGREPORT}/g" < pinentry-dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/pinentry-dmenu.1 58 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/pinentry-dmenu.1 59 | 60 | uninstall: 61 | @echo removing executable from ${DESTDIR}${PREFIX}/bin 62 | @rm -f ${DESTDIR}${PREFIX}/bin/pinentry-dmenu 63 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 64 | @rm -f ${DESTDIR}${MANPREFIX}/man1/pinentry-dmenu.1 65 | 66 | .PHONY: all options clean dist install pinentry uninstall 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pinentry-dmenu 2 | ============== 3 | 4 | pinentry-dmenu is a pinentry program with the charm of [dmenu](https://tools.suckless.org/dmenu). 5 | 6 | This program is a fork from [spine](https://gitgud.io/zavok/spine.git) which is also a fork from [dmenu](https://tools.suckless.org/dmenu). 7 | 8 | 9 | NO FURTHER DEVELOPMENT 10 | ---------------------- 11 | 12 | This project is no longer under development. If you have another opinion feel free to fork it. 13 | 14 | 15 | Requirements 16 | ------------ 17 | In order to build dmenu you need the Xlib header files. 18 | 19 | 20 | Installation 21 | ------------ 22 | Edit config.mk to match your local setup (dmenu is installed into the /usr/local namespace by default). 23 | 24 | Afterwards enter the following command to build and install dmenu 25 | (if necessary as root): 26 | 27 | make clean install 28 | 29 | 30 | Config 31 | ------ 32 | To use pinentry-dmenu add in `~/.gnupg/gpg-agent.conf`: 33 | 34 | pinentry-program 35 | 36 | The config is located in `~/.gnupg/pinentry-dmenu.conf`. 37 | 38 | Parameter | Default | Description 39 | :------------------ |:----------------- |:----------- 40 | asterisk | * | Defines the symbol which is showed for each typed character 41 | bottom | false | pinentry-dmenu appears at the bottom of the screen 42 | min_password_length | 32 | The minimal space of the password field. This value has affect to the description field after the password field 43 | monitor | -1 | pinentry-dmenu is displayed on the monitor number supplied. Monitor numbers are starting from 0 44 | prompt | "" | Defines the prompt to be displayed to the left of the input field 45 | font | monospace:size=10 | Defines the font or font set used 46 | prompt_bg | #bbbbbb | Defines the prompt background color 47 | prompt_fg | #222222 | Defines the prompt foreground color 48 | normal_bg | #bbbbbb | Defines the normal background color 49 | normal_fg | #222222 | Defines the normal foreground color 50 | select_bg | #eeeeee | Defines the selected background color 51 | select_fg | #005577 | Defines the selected foreground color 52 | desc_bg | #bbbbbb | Defines the description background color 53 | desc_fg | #222222 | Defines the description foreground color 54 | embedded | false | Embed into window 55 | 56 | 57 | Example 58 | ------- 59 | ``` 60 | asterisk= "# "; 61 | prompt = "$"; 62 | font = "Noto Sans UI:size=13"; 63 | prompt_fg = "#eeeeee"; 64 | prompt_bg = "#d9904a"; 65 | normal_fg = "#ffffff"; 66 | normal_bg = "#000000"; 67 | select_fg = "#eeeeee"; 68 | select_bg = "#d9904a"; 69 | desc_fg = "#eeeeee"; 70 | desc_bg = "#d9904a"; 71 | ``` 72 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | /* Default settings; can be overriden by command line. */ 3 | 4 | static int bottom = 0; 5 | static int embedded = 0; 6 | static int minpwlen = 32; 7 | static int mon = -1; 8 | static int lineheight = 0; 9 | static int min_lineheight = 8; 10 | 11 | static const char *asterisk = "*"; 12 | static const char *fonts[] = { 13 | "monospace:size=10" 14 | }; 15 | static const char *prompt = NULL; 16 | static const char *colors[SchemeLast][4] = { 17 | [SchemePrompt] = { "#bbbbbb", "#222222" }, 18 | [SchemeNormal] = { "#bbbbbb", "#222222" }, 19 | [SchemeSelect] = { "#eeeeee", "#005577" }, 20 | [SchemeDesc] = { "#bbbbbb", "#222222" } 21 | }; 22 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # Pinentry settings 2 | DATE = $$(date +'%B %Y') 3 | VERSION = 0.1 4 | BUGREPORT = https:\/\/github.com\/ritze\/pinentry-dmenu 5 | 6 | # Paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | X11INC = /usr/X11R6/include 11 | X11LIB = /usr/X11R6/lib 12 | 13 | # Xinerama, comment if you don't want it 14 | XINERAMALIBS = -lXinerama 15 | XINERAMAFLAGS = -DXINERAMA 16 | 17 | # Freetype 18 | FREETYPELIBS = -lfontconfig -lXft 19 | FREETYPEINC = /usr/include/freetype2 20 | # OpenBSD (uncomment) 21 | #FREETYPEINC = ${X11INC}/freetype2 22 | 23 | # Includes and libs 24 | INCS = -I${X11INC} -I${FREETYPEINC} 25 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 26 | 27 | # Flags 28 | CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} -DPACKAGE_VERSION=\"${VERSION}\" -DPACKAGE_BUGREPORT=\"${BUGREPORT}\" 29 | CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} 30 | LDFLAGS = -s ${LIBS} 31 | 32 | # Compiler and linker 33 | CC = cc 34 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drw.h" 9 | #include "util.h" 10 | 11 | #define UTF_INVALID 0xFFFD 12 | #define UTF_SIZ 4 13 | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 18 | 19 | static long 20 | utf8decodebyte(const char c, size_t *i) 21 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | free(drw); 99 | } 100 | 101 | /* This function is an implementation detail. Library users should use 102 | * drw_fontset_create instead. 103 | */ 104 | static Fnt * 105 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 106 | { 107 | Fnt *font; 108 | XftFont *xfont = NULL; 109 | FcPattern *pattern = NULL; 110 | 111 | if (fontname) { 112 | /* Using the pattern found at font->xfont->pattern does not yield the 113 | * same substitution results as using the pattern returned by 114 | * FcNameParse; using the latter results in the desired fallback 115 | * behaviour whereas the former just results in missing-character 116 | * rectangles being drawn, at least with some fonts. */ 117 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 118 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 119 | return NULL; 120 | } 121 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 122 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 123 | XftFontClose(drw->dpy, xfont); 124 | return NULL; 125 | } 126 | } else if (fontpattern) { 127 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 128 | fprintf(stderr, "error, cannot load font from pattern.\n"); 129 | return NULL; 130 | } 131 | } else { 132 | die("no font specified."); 133 | } 134 | 135 | font = ecalloc(1, sizeof(Fnt)); 136 | font->xfont = xfont; 137 | font->pattern = pattern; 138 | font->h = xfont->ascent + xfont->descent; 139 | font->dpy = drw->dpy; 140 | 141 | return font; 142 | } 143 | 144 | static void 145 | xfont_free(Fnt *font) 146 | { 147 | if (!font) 148 | return; 149 | if (font->pattern) 150 | FcPatternDestroy(font->pattern); 151 | XftFontClose(font->dpy, font->xfont); 152 | free(font); 153 | } 154 | 155 | Fnt* 156 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 157 | { 158 | Fnt *cur, *ret = NULL; 159 | size_t i; 160 | 161 | if (!drw || !fonts) 162 | return NULL; 163 | 164 | for (i = 1; i <= fontcount; i++) { 165 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 166 | cur->next = ret; 167 | ret = cur; 168 | } 169 | } 170 | return (drw->fonts = ret); 171 | } 172 | 173 | void 174 | drw_fontset_free(Fnt *font) 175 | { 176 | if (font) { 177 | drw_fontset_free(font->next); 178 | xfont_free(font); 179 | } 180 | } 181 | 182 | void 183 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 184 | { 185 | if (!drw || !dest || !clrname) 186 | return; 187 | 188 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 189 | DefaultColormap(drw->dpy, drw->screen), 190 | clrname, dest)) 191 | die("error, cannot allocate color '%s'", clrname); 192 | } 193 | 194 | /* Wrapper to create color schemes. The caller has to call free(3) on the 195 | * returned color scheme when done using it. */ 196 | Clr * 197 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 198 | { 199 | size_t i; 200 | Clr *ret; 201 | 202 | /* need at least two colors for a scheme */ 203 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 204 | return NULL; 205 | 206 | for (i = 0; i < clrcount; i++) 207 | drw_clr_create(drw, &ret[i], clrnames[i]); 208 | return ret; 209 | } 210 | 211 | void 212 | drw_setfontset(Drw *drw, Fnt *set) 213 | { 214 | if (drw) 215 | drw->fonts = set; 216 | } 217 | 218 | void 219 | drw_setscheme(Drw *drw, Clr *scm) 220 | { 221 | if (drw) 222 | drw->scheme = scm; 223 | } 224 | 225 | void 226 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 227 | { 228 | if (!drw || !drw->scheme) 229 | return; 230 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 231 | if (filled) 232 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 233 | else 234 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 235 | } 236 | 237 | int 238 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 239 | { 240 | char buf[1024]; 241 | int ty; 242 | unsigned int ew; 243 | XftDraw *d = NULL; 244 | Fnt *usedfont, *curfont, *nextfont; 245 | size_t i, len; 246 | int utf8strlen, utf8charlen, render = x || y || w || h; 247 | long utf8codepoint = 0; 248 | const char *utf8str; 249 | FcCharSet *fccharset; 250 | FcPattern *fcpattern; 251 | FcPattern *match; 252 | XftResult result; 253 | int charexists = 0; 254 | 255 | if (!drw || (render && !drw->scheme) || !text || !drw->fonts) 256 | return 0; 257 | 258 | if (!render) { 259 | w = ~w; 260 | } else { 261 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 262 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 263 | d = XftDrawCreate(drw->dpy, drw->drawable, 264 | DefaultVisual(drw->dpy, drw->screen), 265 | DefaultColormap(drw->dpy, drw->screen)); 266 | x += lpad; 267 | w -= lpad; 268 | } 269 | 270 | usedfont = drw->fonts; 271 | while (1) { 272 | utf8strlen = 0; 273 | utf8str = text; 274 | nextfont = NULL; 275 | while (*text) { 276 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 277 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 278 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 279 | if (charexists) { 280 | if (curfont == usedfont) { 281 | utf8strlen += utf8charlen; 282 | text += utf8charlen; 283 | } else { 284 | nextfont = curfont; 285 | } 286 | break; 287 | } 288 | } 289 | 290 | if (!charexists || nextfont) 291 | break; 292 | else 293 | charexists = 0; 294 | } 295 | 296 | if (utf8strlen) { 297 | drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); 298 | /* shorten text if necessary */ 299 | for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) 300 | drw_font_getexts(usedfont, utf8str, len, &ew, NULL); 301 | 302 | if (len) { 303 | memcpy(buf, utf8str, len); 304 | buf[len] = '\0'; 305 | if (len < utf8strlen) 306 | for (i = len; i && i > len - 3; buf[--i] = '.') 307 | ; /* NOP */ 308 | 309 | if (render) { 310 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 311 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 312 | usedfont->xfont, x, ty, (XftChar8 *)buf, len); 313 | } 314 | x += ew; 315 | w -= ew; 316 | } 317 | } 318 | 319 | if (!*text) { 320 | break; 321 | } else if (nextfont) { 322 | charexists = 0; 323 | usedfont = nextfont; 324 | } else { 325 | /* Regardless of whether or not a fallback font is found, the 326 | * character must be drawn. */ 327 | charexists = 1; 328 | 329 | fccharset = FcCharSetCreate(); 330 | FcCharSetAddChar(fccharset, utf8codepoint); 331 | 332 | if (!drw->fonts->pattern) { 333 | /* Refer to the comment in xfont_create for more information. */ 334 | die("the first font in the cache must be loaded from a font string."); 335 | } 336 | 337 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 338 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 339 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 340 | 341 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 342 | FcDefaultSubstitute(fcpattern); 343 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 344 | 345 | FcCharSetDestroy(fccharset); 346 | FcPatternDestroy(fcpattern); 347 | 348 | if (match) { 349 | usedfont = xfont_create(drw, NULL, match); 350 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 351 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 352 | ; /* NOP */ 353 | curfont->next = usedfont; 354 | } else { 355 | xfont_free(usedfont); 356 | usedfont = drw->fonts; 357 | } 358 | } 359 | } 360 | } 361 | if (d) 362 | XftDrawDestroy(d); 363 | 364 | return x + (render ? w : 0); 365 | } 366 | 367 | void 368 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 369 | { 370 | if (!drw) 371 | return; 372 | 373 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 374 | XSync(drw->dpy, False); 375 | } 376 | 377 | unsigned int 378 | drw_fontset_getwidth(Drw *drw, const char *text) 379 | { 380 | if (!drw || !drw->fonts || !text) 381 | return 0; 382 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 383 | } 384 | 385 | void 386 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 387 | { 388 | XGlyphInfo ext; 389 | 390 | if (!font || !text) 391 | return; 392 | 393 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 394 | if (w) 395 | *w = ext.xOff; 396 | if (h) 397 | *h = font->h; 398 | } 399 | 400 | Cur * 401 | drw_cur_create(Drw *drw, int shape) 402 | { 403 | Cur *cur; 404 | 405 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 406 | return NULL; 407 | 408 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 409 | 410 | return cur; 411 | } 412 | 413 | void 414 | drw_cur_free(Drw *drw, Cur *cursor) 415 | { 416 | if (!cursor) 417 | return; 418 | 419 | XFreeCursor(drw->dpy, cursor->cursor); 420 | free(cursor); 421 | } 422 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 39 | 40 | /* Colorscheme abstraction */ 41 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 42 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 43 | 44 | /* Cursor abstraction */ 45 | Cur *drw_cur_create(Drw *drw, int shape); 46 | void drw_cur_free(Drw *drw, Cur *cursor); 47 | 48 | /* Drawing context manipulation */ 49 | void drw_setfontset(Drw *drw, Fnt *set); 50 | void drw_setscheme(Drw *drw, Clr *scm); 51 | 52 | /* Drawing functions */ 53 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 54 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); 55 | 56 | /* Map functions */ 57 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 58 | -------------------------------------------------------------------------------- /pinentry-dmenu.1: -------------------------------------------------------------------------------- 1 | .TH PINENTRY-DMENU 1 "DATE" pinentry-dmenu\-VERSION "pinentry-dmenu Manual" 2 | 3 | 4 | .SH NAME 5 | pinentry-dmenu - a pinentry program with the charm of dmenu 6 | .SH DESCRIPTION 7 | .B pinentry-dmenu 8 | is a dmenu- and pinentry-based passphrase dialog called from the 9 | .BR gpg-agent (1) 10 | daemon. It is not intended to be invoked directly. 11 | 12 | 13 | .SH SYNOPSIS 14 | Set the 15 | .B pinentry-program 16 | in 17 | .IR ~/.gnupg/gpg-agent.conf 18 | to 19 | .B pinentry-dmenu 20 | to use the program as the regular dialog for 21 | .BR gpg-agent . 22 | .PP 23 | The configuration is placed in 24 | .IR ~/.gnupg/pinentry-dmenu.conf . 25 | You can change the path to the config file with the environment variable 26 | .IR GNUPGHOME . 27 | 28 | 29 | .SH OPTIONS 30 | .TP 31 | .BI "asterisk =" " *" 32 | Defines the symbol which is showed for each typed character. 33 | .TP 34 | .BI "bottom =" " false" 35 | pinentry-dmenu appears at the bottom of the screen. 36 | .TP 37 | .BI "min_password_length =" " 32" 38 | The minimal space of the password field. This value has affect to the description field after the password field. 39 | .TP 40 | .BI "height =" " 8" 41 | Height of pinentry-dmenu in pixels (no less than 8) 42 | .TP 43 | .BI "monitor =" " -1" 44 | pinentry-dmenu is displayed on the monitor number supplied. Monitor numbers are starting from 0. 45 | .TP 46 | .BI "prompt =" " """" 47 | Defines the prompt to be displayed to the left of the input field. 48 | .TP 49 | .BI "font =" " monospace:size=10" 50 | Defines the font or font set used. 51 | .TP 52 | .BI "prompt_bg =" " #bbbbbb" 53 | Defines the prompt background color. 54 | .IR #RGB , 55 | .I #RRGGBB 56 | and X color names are supported. 57 | .TP 58 | .BI "prompt_fg =" " #222222" 59 | Defines the prompt foreground color. 60 | .TP 61 | .BI "normal_bg =" " #bbbbbb" 62 | Defines the normal background color. 63 | .TP 64 | .BI "normal_fg =" " #222222" 65 | Defines the normal foreground color. 66 | .TP 67 | .BI "select_bg =" " #eeeeee" 68 | Defines the selected background color. 69 | .TP 70 | .BI "select_fg =" " #005577" 71 | Defines the selected foreground color. 72 | .TP 73 | .BI "desc_bg =" " #bbbbbb" 74 | Defines the description background color. 75 | .TP 76 | .BI "desc_fg =" " #222222" 77 | Defines the description foreground color. 78 | .TP 79 | .BI "embedded =" " false" 80 | Embed into window. 81 | 82 | 83 | .SH USAGE 84 | pinentry-dmenu is completely controlled by the keyboard. 85 | .TP 86 | .B Return 87 | Confirm input 88 | .TP 89 | .B Ctrl-Return 90 | Confirm input 91 | .TP 92 | .B Shift\-Return 93 | Confirm input 94 | .TP 95 | .B Escape 96 | Cancel input 97 | .TP 98 | .B C\-c 99 | Escape 100 | 101 | .SS Confirm Mode 102 | .TP 103 | .B Down 104 | Right 105 | .TP 106 | .B End 107 | Right 108 | .TP 109 | .B Home 110 | Left 111 | .TP 112 | .B Next 113 | Right 114 | .TP 115 | .B Prior 116 | Left 117 | .TP 118 | .B Up 119 | Left 120 | .TP 121 | .B g 122 | Cancel input 123 | .TP 124 | .B G 125 | Cancel input 126 | .TP 127 | .B h 128 | Left 129 | .TP 130 | .B j 131 | Left 132 | .TP 133 | .B k 134 | Right 135 | .TP 136 | .B l 137 | Right 138 | .TP 139 | .B n 140 | Confirm with no 141 | .TP 142 | .B N 143 | Confirm with no 144 | .TP 145 | .B y 146 | Confirm with yes 147 | .TP 148 | .B Y 149 | Confirm with yes 150 | 151 | .SS Pin Mode 152 | .TP 153 | .B End 154 | Move cursor to the line end 155 | .TP 156 | .B Home 157 | Move cursor to the line begin 158 | .TP 159 | .B C\-a 160 | Home 161 | .TP 162 | .B C\-b 163 | Left 164 | .TP 165 | .B C\-d 166 | Delete 167 | .TP 168 | .B C\-e 169 | End 170 | .TP 171 | .B C\-f 172 | Right 173 | .TP 174 | .B C\-g 175 | Escape 176 | .TP 177 | .B C\-h 178 | Backspace 179 | .TP 180 | .B C\-k 181 | Delete line right 182 | .TP 183 | .B C\-u 184 | Delete line left 185 | .TP 186 | .B C\-v 187 | Paste from primary X selection 188 | 189 | 190 | .SH EXAMPLES 191 | .sp 192 | .if n \{ 193 | .RS 4 194 | .\} 195 | .nf 196 | asterisk= "# "; 197 | prompt = "$"; 198 | font = "Noto Sans UI:size=13"; 199 | prompt_fg = "#eeeeee"; 200 | prompt_bg = "#d9904a"; 201 | normal_fg = "#ffffff"; 202 | normal_bg = "#000000"; 203 | select_fg = "#eeeeee"; 204 | select_bg = "#d9904a"; 205 | desc_fg = "#eeeeee"; 206 | desc_bg = "#d9904a"; 207 | 208 | 209 | .SH AUTHORS 210 | .B pinentry-dmenu 211 | is a fork of 212 | .B dmenu 213 | 214 | and uses the api of 215 | .B pinentry 216 | , a GnuPG tool. 217 | .B pinentry-dmenu 218 | itself was written by Moritz Lüdecke . 219 | 220 | 221 | .SH REPORTING BUGS 222 | Report pinentry-dmenu bugs to 223 | 224 | 225 | .SH SEE ALSO 226 | .BR dmenu (1), 227 | .BR dwm (1), 228 | .BR gpg-agent (1) 229 | -------------------------------------------------------------------------------- /pinentry-dmenu.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #ifdef XINERAMA 18 | #include 19 | #endif 20 | #include 21 | 22 | #include "drw.h" 23 | #include "util.h" 24 | 25 | #include "pinentry/pinentry.h" 26 | #include "pinentry/memory.h" 27 | 28 | #define CONFIG_DIR "/.gnupg" 29 | #define CONFIG_FILE "/pinentry-dmenu.conf" 30 | #define INTERSECT(x, y, w, h, r) \ 31 | (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ 32 | && MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) 33 | #define LENGTH(X) (sizeof(X) / sizeof(X[0])) 34 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 35 | #define MINDESCLEN 8 36 | 37 | 38 | enum { SchemePrompt, SchemeNormal, SchemeSelect, SchemeDesc, SchemeLast }; 39 | enum { WinPin, WinConfirm }; 40 | enum { Ok, NotOk, Cancel }; 41 | enum { Nothing, Yes, No }; 42 | 43 | static int bh, mw, mh; 44 | static int sel; 45 | static int promptw, pdescw; 46 | /* Sum of left and right padding */ 47 | static int lrpad; 48 | static size_t cursor; 49 | static int screen; 50 | 51 | static char* pin; 52 | static int pin_len; 53 | static char* pin_repeat; 54 | static int pin_repeat_len; 55 | static int repeat; 56 | 57 | static Atom clip, utf8; 58 | static Display *dpy; 59 | static Window root, parentwin, win; 60 | static XIC xic; 61 | 62 | static Drw *drw; 63 | static Clr *scheme[SchemeLast]; 64 | 65 | static int timed_out; 66 | static int winmode; 67 | pinentry_t pinentry_info; 68 | 69 | #include "config.h" 70 | 71 | static int 72 | drawitem(const char* text, Bool sel, int x, int y, int w) { 73 | unsigned int i = (sel) ? SchemeSelect : SchemeNormal; 74 | 75 | drw_setscheme(drw, scheme[i]); 76 | 77 | return drw_text(drw, x, y, w, bh, lrpad / 2, text, 0); 78 | } 79 | 80 | static void 81 | grabfocus(void) { 82 | Window focuswin; 83 | int i, revertwin; 84 | 85 | for (i = 0; i < 100; ++i) { 86 | XGetInputFocus(dpy, &focuswin, &revertwin); 87 | if (focuswin == win) { 88 | return; 89 | } 90 | XSetInputFocus(dpy, win, RevertToParent, CurrentTime); 91 | usleep(1000); 92 | } 93 | 94 | die("cannot grab focus"); 95 | } 96 | 97 | static void 98 | grabkeyboard(void) { 99 | int i; 100 | 101 | if (embedded) { 102 | return; 103 | } 104 | 105 | /* Try to grab keyboard, 106 | * we may have to wait for another process to ungrab */ 107 | for (i = 0; i < 1000; i++) { 108 | if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, 109 | GrabModeAsync, CurrentTime) == GrabSuccess) { 110 | return; 111 | } 112 | usleep(1000); 113 | } 114 | 115 | die("cannot grab keyboard"); 116 | } 117 | 118 | static size_t 119 | nextrune(int cursor, int inc) { 120 | ssize_t n; 121 | 122 | /* Return location of next utf8 rune in the given direction (+1 or -1) */ 123 | for (n = cursor + inc; 124 | n + inc >= 0 && (pin[n] & 0xc0) == 0x80; 125 | n += inc); 126 | 127 | return n; 128 | } 129 | 130 | static void 131 | setup_pin(char* pin_ptr, int len, int reset) { 132 | pin = pin_ptr; 133 | pin_len = len; 134 | 135 | if (reset) { 136 | promptw = (prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 137 | cursor = 0; 138 | 139 | if (pin) { 140 | pin[0] = '\0'; 141 | } 142 | } 143 | } 144 | 145 | static void 146 | insert(const char *str, ssize_t n) { 147 | size_t len = strlen(pin); 148 | 149 | // FIXME: Pinentry crashes when increasing the pin buffer the second time. 150 | // Other pinentry programs has a limited passwort field length. 151 | if (len + n > pin_len - 1) { 152 | if (repeat) { 153 | pin_repeat_len = 2 * pin_repeat_len; 154 | pin_repeat = secmem_realloc(pin_repeat, pin_repeat_len); 155 | setup_pin(pin_repeat, pin_repeat_len, 0); 156 | if (!pin_repeat) { 157 | pin_len = 0; 158 | } 159 | } else { 160 | if (!pinentry_setbufferlen(pinentry_info, 2 * pinentry_info->pin_len)) { 161 | pin_len = 0; 162 | } else { 163 | setup_pin(pinentry_info->pin, pinentry_info->pin_len, 0); 164 | } 165 | } 166 | if (pin_len == 0) { 167 | printf("Error: Couldn't allocate secure memory\n"); 168 | return; 169 | } 170 | } 171 | 172 | /* Move existing text out of the way, insert new text, and update cursor */ 173 | memmove(&pin[cursor + n], &pin[cursor], pin_len - cursor - MAX(n, 0)); 174 | 175 | if (n > 0) { 176 | memcpy(&pin[cursor], str, n); 177 | } 178 | 179 | cursor += n; 180 | pin[len + n] = '\0'; 181 | } 182 | 183 | static void 184 | drawwin(void) { 185 | unsigned int curpos; 186 | int x = 0, fh = drw->fonts->h, pb, pbw = 0, i; 187 | size_t asterlen = strlen(asterisk); 188 | size_t pdesclen; 189 | int leftinput; 190 | char* censort; 191 | 192 | char* pprompt = (repeat) ? pinentry_info->repeat_passphrase : pinentry_info->prompt; 193 | int ppromptw = (pprompt) ? TEXTW(pprompt) : 0; 194 | 195 | unsigned int censortl = minpwlen * TEXTW(asterisk) / strlen(asterisk); 196 | unsigned int confirml = TEXTW(" YesNo ") + 3 * lrpad; 197 | 198 | drw_setscheme(drw, scheme[SchemeNormal]); 199 | drw_rect(drw, 0, 0, mw, mh, 1, 1); 200 | 201 | if (prompt) { 202 | drw_setscheme(drw, scheme[SchemePrompt]); 203 | x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); 204 | } 205 | 206 | if (pprompt) { 207 | drw_setscheme(drw, scheme[SchemePrompt]); 208 | drw_text(drw, x, 0, ppromptw, bh, lrpad / 2, pprompt, 0); 209 | x += ppromptw; 210 | } 211 | 212 | if (pinentry_info->description) { 213 | pb = mw - x; 214 | pdesclen = strlen(pinentry_info->description); 215 | 216 | if (pb > 0) { 217 | pb -= (winmode == WinPin) ? censortl : confirml; 218 | pbw = MINDESCLEN * pdescw / pdesclen; 219 | pbw = MIN(pbw, pdescw); 220 | 221 | if (pb >= pbw) { 222 | pbw = MAX(pbw, pdescw); 223 | pbw = MIN(pbw, pb); 224 | pb = mw - pbw; 225 | 226 | for (i = 0; i < pdesclen; i++) { 227 | if (pinentry_info->description[i] == '\n') { 228 | pinentry_info->description[i] = ' '; 229 | } 230 | } 231 | 232 | drw_setscheme(drw, scheme[SchemeDesc]); 233 | drw_text(drw, pb, 0, pbw, bh, lrpad / 2, pinentry_info->description, 234 | 0); 235 | } else { 236 | pbw = 0; 237 | } 238 | } 239 | } 240 | 241 | /* Draw input field */ 242 | drw_setscheme(drw, scheme[SchemeNormal]); 243 | 244 | if (winmode == WinPin) { 245 | censort = ecalloc(1, asterlen * pin_len); 246 | 247 | for (i = 0; i < asterlen * strlen(pin); i += asterlen) { 248 | memcpy(&censort[i], asterisk, asterlen); 249 | } 250 | 251 | censort[i+1] = '\n'; 252 | leftinput = mw - x - pbw; 253 | drw_text(drw, x, 0, leftinput, bh, lrpad / 2, censort, 0); 254 | drw_font_getexts(drw->fonts, censort, cursor * asterlen, &curpos, NULL); 255 | 256 | if ((curpos += lrpad / 2 - 1) < leftinput) { 257 | drw_setscheme(drw, scheme[SchemeNormal]); 258 | drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0); 259 | } 260 | 261 | free(censort); 262 | } else { 263 | x += TEXTW(" "); 264 | x = drawitem("No", (sel == No), x, 0, TEXTW("No")); 265 | x = drawitem("Yes", (sel == Yes), x, 0, TEXTW("Yes")); 266 | } 267 | 268 | drw_map(drw, win, 0, 0, mw, mh); 269 | } 270 | 271 | static void 272 | setup(void) { 273 | int x, y, i = 0; 274 | unsigned int du; 275 | XSetWindowAttributes swa; 276 | XIM xim; 277 | Window w, dw, *dws; 278 | XWindowAttributes wa; 279 | #ifdef XINERAMA 280 | XineramaScreenInfo *info; 281 | Window pw; 282 | int a, j, di, n, area = 0; 283 | #endif 284 | 285 | /* Init appearance */ 286 | scheme[SchemePrompt] = drw_scm_create(drw, colors[SchemePrompt], 2); 287 | scheme[SchemeNormal] = drw_scm_create(drw, colors[SchemeNormal], 2); 288 | scheme[SchemeSelect] = drw_scm_create(drw, colors[SchemeSelect], 2); 289 | scheme[SchemeDesc] = drw_scm_create(drw, colors[SchemeDesc], 2); 290 | 291 | clip = XInternAtom(dpy, "CLIPBOARD", False); 292 | utf8 = XInternAtom(dpy, "UTF8_STRING", False); 293 | 294 | /* Calculate menu geometry */ 295 | bh = drw->fonts->h + 2; 296 | bh = MAX(bh, lineheight); 297 | mh = bh; 298 | #ifdef XINERAMA 299 | info = XineramaQueryScreens(dpy, &n); 300 | 301 | if (parentwin == root && info) { 302 | XGetInputFocus(dpy, &w, &di); 303 | if (mon >= 0 && mon < n) { 304 | i = mon; 305 | } else if (w != root && w != PointerRoot && w != None) { 306 | /* Find top-level window containing current input focus */ 307 | do { 308 | if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) { 309 | XFree(dws); 310 | } 311 | } while (w != root && w != pw); 312 | /* Find xinerama screen with which the window intersects most */ 313 | if (XGetWindowAttributes(dpy, pw, &wa)) { 314 | for (j = 0; j < n; j++) { 315 | a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j]); 316 | if (a > area) { 317 | area = a; 318 | i = j; 319 | } 320 | } 321 | } 322 | } 323 | /* No focused window is on screen, so use pointer location instead */ 324 | if (mon < 0 && !area 325 | && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) { 326 | for (i = 0; i < n; i++) { 327 | if (INTERSECT(x, y, 1, 1, info[i])) { 328 | break; 329 | } 330 | } 331 | } 332 | 333 | x = info[i].x_org; 334 | y = info[i].y_org + (bottom ? info[i].height - mh : 0); 335 | mw = info[i].width; 336 | XFree(info); 337 | } else 338 | #endif 339 | { 340 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) { 341 | die("could not get embedding window attributes: 0x%lx", parentwin); 342 | } 343 | x = 0; 344 | y = bottom ? wa.height - mh : 0; 345 | mw = wa.width; 346 | } 347 | 348 | pdescw = (pinentry_info->description) ? TEXTW(pinentry_info->description) : 0; 349 | 350 | /* Create menu window */ 351 | swa.override_redirect = True; 352 | swa.background_pixel = scheme[SchemePrompt][ColBg].pixel; 353 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; 354 | win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, 355 | CopyFromParent, CopyFromParent, CopyFromParent, 356 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 357 | 358 | /* Open input methods */ 359 | xim = XOpenIM(dpy, NULL, NULL, NULL); 360 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 361 | XNClientWindow, win, XNFocusWindow, win, NULL); 362 | XMapRaised(dpy, win); 363 | 364 | if (embedded) { 365 | XSelectInput(dpy, parentwin, FocusChangeMask); 366 | 367 | if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { 368 | for (i = 0; i < du && dws[i] != win; ++i) { 369 | XSelectInput(dpy, dws[i], FocusChangeMask); 370 | } 371 | 372 | XFree(dws); 373 | } 374 | grabfocus(); 375 | } 376 | 377 | drw_resize(drw, mw, mh); 378 | } 379 | 380 | static void 381 | cleanup(void) { 382 | XUngrabKey(dpy, AnyKey, AnyModifier, root); 383 | free(scheme[SchemeDesc]); 384 | free(scheme[SchemeSelect]); 385 | free(scheme[SchemeNormal]); 386 | free(scheme[SchemePrompt]); 387 | drw_free(drw); 388 | XSync(dpy, False); 389 | XCloseDisplay(dpy); 390 | } 391 | 392 | static int 393 | keypress_confirm(XKeyEvent *ev, KeySym ksym) { 394 | if (ev->state & ControlMask) { 395 | switch(ksym) { 396 | case XK_c: 397 | pinentry_info->canceled = 1; 398 | sel = No; 399 | return 1; 400 | default: 401 | return 1; 402 | } 403 | } 404 | 405 | switch(ksym) { 406 | case XK_KP_Enter: 407 | case XK_Return: 408 | if (sel != Nothing) { 409 | return 1; 410 | } 411 | break; 412 | case XK_y: 413 | case XK_Y: 414 | sel = Yes; 415 | return 1; 416 | case XK_n: 417 | case XK_N: 418 | sel = No; 419 | return 1; 420 | case XK_g: 421 | case XK_G: 422 | case XK_Escape: 423 | pinentry_info->canceled = 1; 424 | sel = No; 425 | return 1; 426 | case XK_h: 427 | case XK_j: 428 | case XK_Home: 429 | case XK_Left: 430 | case XK_Prior: 431 | case XK_Up: 432 | sel = No; 433 | break; 434 | case XK_k: 435 | case XK_l: 436 | case XK_Down: 437 | case XK_End: 438 | case XK_Next: 439 | case XK_Right: 440 | sel = Yes; 441 | break; 442 | } 443 | 444 | return 0; 445 | } 446 | 447 | static int 448 | keypress_pin(XKeyEvent *ev, KeySym ksym, char* buf, int len) { 449 | int old; 450 | 451 | if (ev->state & ControlMask) { 452 | switch(ksym) { 453 | case XK_a: ksym = XK_Home; break; 454 | case XK_b: ksym = XK_Left; break; 455 | case XK_c: ksym = XK_Escape; break; 456 | case XK_d: ksym = XK_Delete; break; 457 | case XK_e: ksym = XK_End; break; 458 | case XK_f: ksym = XK_Right; break; 459 | case XK_g: ksym = XK_Escape; break; 460 | case XK_h: ksym = XK_BackSpace; break; 461 | case XK_k: 462 | old = cursor; 463 | cursor = strlen(pin); 464 | insert(NULL, old - cursor); 465 | break; 466 | case XK_u: 467 | insert(NULL, -cursor); 468 | break; 469 | case XK_v: 470 | XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 471 | utf8, utf8, win, CurrentTime); 472 | return 0; 473 | case XK_Return: 474 | case XK_KP_Enter: 475 | break; 476 | case XK_bracketleft: 477 | pinentry_info->canceled = 1; 478 | return 1; 479 | default: 480 | return 1; 481 | } 482 | } 483 | 484 | switch(ksym) { 485 | case XK_Delete: 486 | if (pin[cursor] == '\0') { 487 | return 0; 488 | } 489 | cursor = nextrune(cursor, +1); 490 | /* Fallthrough */ 491 | case XK_BackSpace: 492 | if (cursor == 0) { 493 | return 0; 494 | } 495 | insert(NULL, nextrune(cursor, -1) - cursor); 496 | break; 497 | case XK_Escape: 498 | pinentry_info->canceled = 1; 499 | return 1; 500 | case XK_Left: 501 | if (cursor > 0) { 502 | cursor = nextrune(cursor, -1); 503 | } 504 | break; 505 | case XK_Right: 506 | if (pin[cursor] != '\0') { 507 | cursor = nextrune(cursor, +1); 508 | } 509 | break; 510 | case XK_Home: 511 | cursor = 0; 512 | break; 513 | case XK_End: 514 | cursor = strlen(pin); 515 | break; 516 | case XK_Return: 517 | case XK_KP_Enter: 518 | return 1; 519 | break; 520 | default: 521 | if (!iscntrl(*buf)) { 522 | insert(buf, len); 523 | } 524 | } 525 | 526 | return 0; 527 | } 528 | 529 | static int 530 | keypress(XKeyEvent *ev) { 531 | char buf[32]; 532 | int len; 533 | int ret = 1; 534 | 535 | KeySym ksym = NoSymbol; 536 | Status status; 537 | len = XmbLookupString(xic, ev, buf, sizeof(buf), &ksym, &status); 538 | 539 | if (status != XBufferOverflow) { 540 | if (winmode == WinConfirm) { 541 | ret = keypress_confirm(ev, ksym); 542 | } else { 543 | ret = keypress_pin(ev, ksym, buf, len); 544 | } 545 | 546 | if (ret == 0) { 547 | drawwin(); 548 | } 549 | } 550 | 551 | return ret; 552 | } 553 | 554 | static void 555 | paste(void) { 556 | char *p, *q; 557 | int di; 558 | unsigned long dl; 559 | Atom da; 560 | 561 | /* We have been given the current selection, now insert it into input */ 562 | XGetWindowProperty(dpy, win, utf8, 0, pin_len / 4, False, utf8, &da, &di, 563 | &dl, &dl, (unsigned char **)&p); 564 | insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t) strlen(p)); 565 | XFree(p); 566 | drawwin(); 567 | } 568 | 569 | void 570 | run(void) { 571 | XEvent ev; 572 | 573 | drawwin(); 574 | 575 | while (!XNextEvent(dpy, &ev)) { 576 | if (XFilterEvent(&ev, win)) { 577 | continue; 578 | } 579 | switch(ev.type) { 580 | case Expose: 581 | if (ev.xexpose.count == 0) { 582 | drw_map(drw, win, 0, 0, mw, mh); 583 | } 584 | break; 585 | case KeyPress: 586 | if (keypress(&ev.xkey)) { 587 | return; 588 | } 589 | break; 590 | case SelectionNotify: 591 | if (ev.xselection.property == utf8) { 592 | paste(); 593 | } 594 | break; 595 | case VisibilityNotify: 596 | if (ev.xvisibility.state != VisibilityUnobscured) { 597 | XRaiseWindow(dpy, win); 598 | } 599 | break; 600 | } 601 | } 602 | } 603 | 604 | static void 605 | catchsig(int sig) { 606 | if (sig == SIGALRM) { 607 | timed_out = 1; 608 | } 609 | } 610 | 611 | static void 612 | password(void) { 613 | winmode = WinPin; 614 | repeat = 0; 615 | setup_pin(pinentry_info->pin, pinentry_info->pin_len, 1); 616 | run(); 617 | 618 | if (!pinentry_info->canceled && pinentry_info->repeat_passphrase) { 619 | repeat = 1; 620 | pin_repeat_len = pinentry_info->pin_len; 621 | pin_repeat = secmem_malloc(pinentry_info->pin_len); 622 | setup_pin(pin_repeat, pin_repeat_len, 1); 623 | run(); 624 | 625 | pinentry_info->repeat_okay = (strcmp(pinentry_info->pin, pin_repeat) == 0)? 1 : 0; 626 | secmem_free(pin_repeat); 627 | 628 | if (!pinentry_info->repeat_okay) { 629 | pinentry_info->result = -1; 630 | return; 631 | } 632 | } 633 | 634 | if (pinentry_info->canceled) { 635 | pinentry_info->result = -1; 636 | return; 637 | } 638 | 639 | pinentry_info->result = strlen(pinentry_info->pin); 640 | } 641 | 642 | static void 643 | confirm(void) { 644 | winmode = WinConfirm; 645 | sel = Nothing; 646 | run(); 647 | pinentry_info->result = sel != No; 648 | } 649 | 650 | static int 651 | cmdhandler(pinentry_t received_pinentry) { 652 | struct sigaction sa; 653 | XWindowAttributes wa; 654 | 655 | pinentry_info = received_pinentry; 656 | 657 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) { 658 | fputs("warning: no locale support\n", stderr); 659 | } 660 | if (!(dpy = XOpenDisplay(pinentry_info->display))) { 661 | die("cannot open display"); 662 | } 663 | screen = DefaultScreen(dpy); 664 | root = RootWindow(dpy, screen); 665 | embedded = (pinentry_info->parent_wid) ? embedded : 0; 666 | parentwin = (embedded) ? pinentry_info->parent_wid : root; 667 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) { 668 | die("could not get embedding window attributes: 0x%lx", parentwin); 669 | } 670 | drw = drw_create(dpy, screen, root, wa.width, wa.height); 671 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) { 672 | die("no fonts could be loaded."); 673 | } 674 | lrpad = drw->fonts->h; 675 | drw_setscheme(drw, scheme[SchemePrompt]); 676 | 677 | if (pinentry_info->timeout) { 678 | memset(&sa, 0, sizeof(sa)); 679 | sa.sa_handler = catchsig; 680 | sigaction(SIGALRM, &sa, NULL); 681 | alarm(pinentry_info->timeout); 682 | } 683 | 684 | grabkeyboard(); 685 | setup(); 686 | 687 | if (pinentry_info->pin) { 688 | do { 689 | password(); 690 | } while (!pinentry_info->canceled && pinentry_info->repeat_passphrase 691 | && !pinentry_info->repeat_okay); 692 | } else { 693 | confirm(); 694 | } 695 | 696 | cleanup(); 697 | 698 | return pinentry_info->result; 699 | } 700 | 701 | pinentry_cmd_handler_t pinentry_cmd_handler = cmdhandler; 702 | 703 | int 704 | main(int argc, char *argv[]) { 705 | Bool bval; 706 | int i, val; 707 | const char *str; 708 | struct passwd *pw; 709 | char path[PATH_MAX]; 710 | char *sudo_uid = getenv("SUDO_UID"); 711 | char *home = getenv("HOME"); 712 | char *gnupghome = getenv("GNUPGHOME"); 713 | config_t cfg; 714 | 715 | if (gnupghome) { 716 | i = strlen(gnupghome); 717 | strcpy(path, gnupghome); 718 | } else { 719 | /* Get the home dir even if the user used sudo or logged in as root */ 720 | if (sudo_uid) { 721 | i = atoi(sudo_uid); 722 | pw = getpwuid(i); 723 | home = pw->pw_dir; 724 | } 725 | 726 | i = strlen(home); 727 | strcpy(path, home); 728 | strcpy(&path[i], CONFIG_DIR); 729 | i += strlen(CONFIG_DIR); 730 | } 731 | 732 | strcpy(&path[i], CONFIG_FILE); 733 | endpwent(); 734 | 735 | config_init(&cfg); 736 | 737 | /* Read the file. If there is an error, report it and exit. */ 738 | if (config_read_file(&cfg, path)) { 739 | if (config_lookup_string(&cfg, "asterisk", &str)) { 740 | asterisk = str; 741 | } 742 | if (config_lookup_bool(&cfg, "bottom", &bval)) { 743 | bottom = bval; 744 | } 745 | if (config_lookup_int(&cfg, "min_password_length", &val)) { 746 | minpwlen = val; 747 | } 748 | if (config_lookup_int(&cfg, "height", &val)) { 749 | lineheight = MAX(val, min_lineheight); 750 | } 751 | if (config_lookup_int(&cfg, "monitor", &val)) { 752 | mon = val; 753 | } 754 | if (config_lookup_string(&cfg, "prompt", &str)) { 755 | prompt = str; 756 | } 757 | if (config_lookup_string(&cfg, "font", &str)) { 758 | fonts[0] = str; 759 | } 760 | if (config_lookup_string(&cfg, "prompt_bg", &str)) { 761 | colors[SchemePrompt][ColBg] = str; 762 | } 763 | if (config_lookup_string(&cfg, "prompt_fg", &str)) { 764 | colors[SchemePrompt][ColFg] = str; 765 | } 766 | if (config_lookup_string(&cfg, "normal_bg", &str)) { 767 | colors[SchemeNormal][ColBg] = str; 768 | } 769 | if (config_lookup_string(&cfg, "normal_fg", &str)) { 770 | colors[SchemeNormal][ColFg] = str; 771 | } 772 | if (config_lookup_string(&cfg, "select_bg", &str)) { 773 | colors[SchemeSelect][ColBg] = str; 774 | } 775 | if (config_lookup_string(&cfg, "select_fg", &str)) { 776 | colors[SchemeSelect][ColFg] = str; 777 | } 778 | if (config_lookup_string(&cfg, "desc_bg", &str)) { 779 | colors[SchemeDesc][ColBg] = str; 780 | } 781 | if (config_lookup_string(&cfg, "desc_fg", &str)) { 782 | colors[SchemeDesc][ColFg] = str; 783 | } 784 | if (config_lookup_bool(&cfg, "embedded", &bval)) { 785 | embedded = bval; 786 | } 787 | } else if ((str = config_error_file(&cfg))) { 788 | fprintf(stderr, "%s:%d: %s\n", config_error_file(&cfg), 789 | config_error_line(&cfg), config_error_text(&cfg)); 790 | return(EXIT_FAILURE); 791 | } 792 | 793 | pinentry_init("pinentry-dmenu"); 794 | pinentry_parse_opts(argc, argv); 795 | 796 | if (pinentry_loop()) { 797 | return 1; 798 | } 799 | 800 | config_destroy(&cfg); 801 | 802 | return 0; 803 | } 804 | -------------------------------------------------------------------------------- /pinentry/AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritze/pinentry-dmenu/51d6656eacc5269388d1eeb5c22579525aeaed30/pinentry/AUTHORS -------------------------------------------------------------------------------- /pinentry/COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 675 Mass Ave, Cambridge, MA 02139, USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | -------------------------------------------------------------------------------- /pinentry/Makefile: -------------------------------------------------------------------------------- 1 | include ../config.mk 2 | 3 | SRC = util.c pinentry.c argparse.c password-cache.c secmem.c 4 | OBJ = ${SRC:.c=.o} 5 | CFLAGS += -DHAVE_MLOCK 6 | 7 | all: pinentry 8 | 9 | .c.o: 10 | @echo CC $< 11 | @${CC} -c ${CFLAGS} $< 12 | 13 | ${OBJ}: pinentry.h argparse.h password-cache.h memory.h util.h 14 | 15 | pinentry: pinentry.o argparse.o password-cache.o secmem.o util.o 16 | 17 | clean: 18 | @echo cleaning 19 | @rm -f ${OBJ} 20 | 21 | .PHONY: all clean pinentry 22 | -------------------------------------------------------------------------------- /pinentry/argparse.c: -------------------------------------------------------------------------------- 1 | /* [argparse.c wk 17.06.97] Argument Parser for option handling 2 | * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc. 3 | * Copyright (C) 1997-2001, 2006-2008, 2013-2015 Werner Koch 4 | * 5 | * This file is part of JNLIB, which is a subsystem of GnuPG. 6 | * 7 | * JNLIB is free software; you can redistribute it and/or modify it 8 | * under the terms of either 9 | * 10 | * - the GNU Lesser General Public License as published by the Free 11 | * Software Foundation; either version 3 of the License, or (at 12 | * your option) any later version. 13 | * 14 | * or 15 | * 16 | * - the GNU General Public License as published by the Free 17 | * Software Foundation; either version 2 of the License, or (at 18 | * your option) any later version. 19 | * 20 | * or both in parallel, as here. 21 | * 22 | * JNLIB is distributed in the hope that it will be useful, but 23 | * WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 | * General Public License for more details. 26 | * 27 | * You should have received a copies of the GNU General Public License 28 | * and the GNU Lesser General Public License along with this program; 29 | * if not, see . 30 | */ 31 | 32 | /* This file may be used as part of GnuPG or standalone. A GnuPG 33 | build is detected by the presence of the macro GNUPG_MAJOR_VERSION. 34 | Some feature are only availalbe in the GnuPG build mode. 35 | */ 36 | 37 | #ifdef HAVE_CONFIG_H 38 | #include 39 | #endif 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #ifdef GNUPG_MAJOR_VERSION 50 | # include "libjnlib-config.h" 51 | # include "mischelp.h" 52 | # include "stringhelp.h" 53 | # include "logging.h" 54 | # ifdef JNLIB_NEED_UTF8CONV 55 | # include "utf8conv.h" 56 | # endif 57 | #endif /*GNUPG_MAJOR_VERSION*/ 58 | 59 | #include "argparse.h" 60 | 61 | /* GnuPG uses GPLv3+ but a standalone version of this defaults to 62 | GPLv2+ because that is the license of this file. Change this if 63 | you include it in a program which uses GPLv3. If you don't want to 64 | set a a copyright string for your usage() you may also hardcode it 65 | here. */ 66 | #ifndef GNUPG_MAJOR_VERSION 67 | 68 | # define ARGPARSE_GPL_VERSION 2 69 | # define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME" 70 | 71 | #else /* Used by GnuPG */ 72 | 73 | # define ARGPARSE_GPL_VERSION 3 74 | # define ARGPARSE_CRIGHT_STR "Copyright (C) 2015 Free Software Foundation, Inc." 75 | 76 | #endif /*GNUPG_MAJOR_VERSION*/ 77 | 78 | /* Replacements for standalone builds. */ 79 | #ifndef GNUPG_MAJOR_VERSION 80 | # ifndef _ 81 | # define _(a) (a) 82 | # endif 83 | # ifndef DIM 84 | # define DIM(v) (sizeof(v)/sizeof((v)[0])) 85 | # endif 86 | # define jnlib_malloc(a) malloc ((a)) 87 | # define jnlib_realloc(a,b) realloc ((a), (b)) 88 | # define jnlib_strdup(a) strdup ((a)) 89 | # define jnlib_free(a) free ((a)) 90 | # define jnlib_log_error my_log_error 91 | # define jnlib_log_bug my_log_bug 92 | # define trim_spaces(a) my_trim_spaces ((a)) 93 | # define map_static_macro_string(a) (a) 94 | #endif /*!GNUPG_MAJOR_VERSION*/ 95 | 96 | 97 | #define ARGPARSE_STR(v) #v 98 | #define ARGPARSE_STR2(v) ARGPARSE_STR(v) 99 | 100 | 101 | /* Replacements for standalone builds. */ 102 | #ifndef GNUPG_MAJOR_VERSION 103 | static void 104 | my_log_error (const char *fmt, ...) 105 | { 106 | va_list arg_ptr ; 107 | 108 | va_start (arg_ptr, fmt); 109 | fprintf (stderr, "%s: ", strusage (11)); 110 | vfprintf (stderr, fmt, arg_ptr); 111 | va_end (arg_ptr); 112 | } 113 | 114 | static void 115 | my_log_bug (const char *fmt, ...) 116 | { 117 | va_list arg_ptr ; 118 | 119 | va_start (arg_ptr, fmt); 120 | fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11)); 121 | vfprintf (stderr, fmt, arg_ptr); 122 | va_end (arg_ptr); 123 | abort (); 124 | } 125 | 126 | static char * 127 | my_trim_spaces (char *str) 128 | { 129 | char *string, *p, *mark; 130 | 131 | string = str; 132 | /* Find first non space character. */ 133 | for (p=string; *p && isspace (*(unsigned char*)p) ; p++) 134 | ; 135 | /* Move characters. */ 136 | for ((mark = NULL); (*string = *p); string++, p++) 137 | if (isspace (*(unsigned char*)p)) 138 | { 139 | if (!mark) 140 | mark = string; 141 | } 142 | else 143 | mark = NULL; 144 | if (mark) 145 | *mark = '\0' ; /* Remove trailing spaces. */ 146 | 147 | return str ; 148 | } 149 | 150 | #endif /*!GNUPG_MAJOR_VERSION*/ 151 | 152 | 153 | 154 | /********************************* 155 | * @Summary arg_parse 156 | * #include "argparse.h" 157 | * 158 | * typedef struct { 159 | * char *argc; pointer to argc (value subject to change) 160 | * char ***argv; pointer to argv (value subject to change) 161 | * unsigned flags; Global flags (DO NOT CHANGE) 162 | * int err; print error about last option 163 | * 1 = warning, 2 = abort 164 | * int r_opt; return option 165 | * int r_type; type of return value (0 = no argument found) 166 | * union { 167 | * int ret_int; 168 | * long ret_long 169 | * ulong ret_ulong; 170 | * char *ret_str; 171 | * } r; Return values 172 | * struct { 173 | * int idx; 174 | * const char *last; 175 | * void *aliases; 176 | * } internal; DO NOT CHANGE 177 | * } ARGPARSE_ARGS; 178 | * 179 | * typedef struct { 180 | * int short_opt; 181 | * const char *long_opt; 182 | * unsigned flags; 183 | * } ARGPARSE_OPTS; 184 | * 185 | * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts ); 186 | * 187 | * @Description 188 | * This is my replacement for getopt(). See the example for a typical usage. 189 | * Global flags are: 190 | * Bit 0 : Do not remove options form argv 191 | * Bit 1 : Do not stop at last option but return other args 192 | * with r_opt set to -1. 193 | * Bit 2 : Assume options and real args are mixed. 194 | * Bit 3 : Do not use -- to stop option processing. 195 | * Bit 4 : Do not skip the first arg. 196 | * Bit 5 : allow usage of long option with only one dash 197 | * Bit 6 : ignore --version 198 | * all other bits must be set to zero, this value is modified by the 199 | * function, so assume this is write only. 200 | * Local flags (for each option): 201 | * Bit 2-0 : 0 = does not take an argument 202 | * 1 = takes int argument 203 | * 2 = takes string argument 204 | * 3 = takes long argument 205 | * 4 = takes ulong argument 206 | * Bit 3 : argument is optional (r_type will the be set to 0) 207 | * Bit 4 : allow 0x etc. prefixed values. 208 | * Bit 6 : Ignore this option 209 | * Bit 7 : This is a command and not an option 210 | * You stop the option processing by setting opts to NULL, the function will 211 | * then return 0. 212 | * @Return Value 213 | * Returns the args.r_opt or 0 if ready 214 | * r_opt may be -2/-7 to indicate an unknown option/command. 215 | * @See Also 216 | * ArgExpand 217 | * @Notes 218 | * You do not need to process the options 'h', '--help' or '--version' 219 | * because this function includes standard help processing; but if you 220 | * specify '-h', '--help' or '--version' you have to do it yourself. 221 | * The option '--' stops argument processing; if bit 1 is set the function 222 | * continues to return normal arguments. 223 | * To process float args or unsigned args you must use a string args and do 224 | * the conversion yourself. 225 | * @Example 226 | * 227 | * ARGPARSE_OPTS opts[] = { 228 | * { 'v', "verbose", 0 }, 229 | * { 'd', "debug", 0 }, 230 | * { 'o', "output", 2 }, 231 | * { 'c', "cross-ref", 2|8 }, 232 | * { 'm', "my-option", 1|8 }, 233 | * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE}, 234 | * { 500, "have-no-short-option-for-this-long-option", 0 }, 235 | * {0} }; 236 | * ARGPARSE_ARGS pargs = { &argc, &argv, 0 } 237 | * 238 | * while( ArgParse( &pargs, &opts) ) { 239 | * switch( pargs.r_opt ) { 240 | * case 'v': opt.verbose++; break; 241 | * case 'd': opt.debug++; break; 242 | * case 'o': opt.outfile = pargs.r.ret_str; break; 243 | * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; 244 | * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; 245 | * case 500: opt.a_long_one++; break 246 | * default : pargs.err = 1; break; -- force warning output -- 247 | * } 248 | * } 249 | * if( argc > 1 ) 250 | * log_fatal( "Too many args"); 251 | * 252 | */ 253 | 254 | typedef struct alias_def_s *ALIAS_DEF; 255 | struct alias_def_s { 256 | ALIAS_DEF next; 257 | char *name; /* malloced buffer with name, \0, value */ 258 | const char *value; /* ptr into name */ 259 | }; 260 | 261 | 262 | /* Object to store the names for the --ignore-invalid-option option. 263 | This is a simple linked list. */ 264 | typedef struct iio_item_def_s *IIO_ITEM_DEF; 265 | struct iio_item_def_s 266 | { 267 | IIO_ITEM_DEF next; 268 | char name[1]; /* String with the long option name. */ 269 | }; 270 | 271 | static const char *(*strusage_handler)( int ) = NULL; 272 | static int (*custom_outfnc) (int, const char *); 273 | 274 | static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s); 275 | static void show_help(ARGPARSE_OPTS *opts, unsigned flags); 276 | static void show_version(void); 277 | static int writestrings (int is_error, const char *string, ...) 278 | #if __GNUC__ >= 4 279 | __attribute__ ((sentinel(0))) 280 | #endif 281 | ; 282 | 283 | 284 | void 285 | argparse_register_outfnc (int (*fnc)(int, const char *)) 286 | { 287 | custom_outfnc = fnc; 288 | } 289 | 290 | 291 | /* Write STRING and all following const char * arguments either to 292 | stdout or, if IS_ERROR is set, to stderr. The list of strings must 293 | be terminated by a NULL. */ 294 | static int 295 | writestrings (int is_error, const char *string, ...) 296 | { 297 | va_list arg_ptr; 298 | const char *s; 299 | int count = 0; 300 | 301 | if (string) 302 | { 303 | s = string; 304 | va_start (arg_ptr, string); 305 | do 306 | { 307 | if (custom_outfnc) 308 | custom_outfnc (is_error? 2:1, s); 309 | else 310 | fputs (s, is_error? stderr : stdout); 311 | count += strlen (s); 312 | } 313 | while ((s = va_arg (arg_ptr, const char *))); 314 | va_end (arg_ptr); 315 | } 316 | return count; 317 | } 318 | 319 | 320 | static void 321 | flushstrings (int is_error) 322 | { 323 | if (custom_outfnc) 324 | custom_outfnc (is_error? 2:1, NULL); 325 | else 326 | fflush (is_error? stderr : stdout); 327 | } 328 | 329 | 330 | static void 331 | initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) 332 | { 333 | if( !(arg->flags & (1<<15)) ) 334 | { 335 | /* Initialize this instance. */ 336 | arg->internal.idx = 0; 337 | arg->internal.last = NULL; 338 | arg->internal.inarg = 0; 339 | arg->internal.stopped = 0; 340 | arg->internal.aliases = NULL; 341 | arg->internal.cur_alias = NULL; 342 | arg->internal.iio_list = NULL; 343 | arg->err = 0; 344 | arg->flags |= 1<<15; /* Mark as initialized. */ 345 | if ( *arg->argc < 0 ) 346 | jnlib_log_bug ("invalid argument for arg_parse\n"); 347 | } 348 | 349 | 350 | if (arg->err) 351 | { 352 | /* Last option was erroneous. */ 353 | const char *s; 354 | 355 | if (filename) 356 | { 357 | if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) 358 | s = _("argument not expected"); 359 | else if ( arg->r_opt == ARGPARSE_READ_ERROR ) 360 | s = _("read error"); 361 | else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG ) 362 | s = _("keyword too long"); 363 | else if ( arg->r_opt == ARGPARSE_MISSING_ARG ) 364 | s = _("missing argument"); 365 | else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) 366 | s = _("invalid argument"); 367 | else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) 368 | s = _("invalid command"); 369 | else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS ) 370 | s = _("invalid alias definition"); 371 | else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) 372 | s = _("out of core"); 373 | else 374 | s = _("invalid option"); 375 | jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s); 376 | } 377 | else 378 | { 379 | s = arg->internal.last? arg->internal.last:"[??]"; 380 | 381 | if ( arg->r_opt == ARGPARSE_MISSING_ARG ) 382 | jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s); 383 | else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) 384 | jnlib_log_error (_("invalid argument for option \"%.50s\"\n"), s); 385 | else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) 386 | jnlib_log_error (_("option \"%.50s\" does not expect an " 387 | "argument\n"), s ); 388 | else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) 389 | jnlib_log_error (_("invalid command \"%.50s\"\n"), s); 390 | else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) 391 | jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s); 392 | else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND ) 393 | jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s ); 394 | else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) 395 | jnlib_log_error ("%s\n", _("out of core\n")); 396 | else 397 | jnlib_log_error (_("invalid option \"%.50s\"\n"), s); 398 | } 399 | if (arg->err != ARGPARSE_PRINT_WARNING) 400 | exit (2); 401 | arg->err = 0; 402 | } 403 | 404 | /* Zero out the return value union. */ 405 | arg->r.ret_str = NULL; 406 | arg->r.ret_long = 0; 407 | } 408 | 409 | 410 | static void 411 | store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) 412 | { 413 | /* TODO: replace this dummy function with a rea one 414 | * and fix the probelms IRIX has with (ALIAS_DEV)arg.. 415 | * used as lvalue 416 | */ 417 | (void)arg; 418 | (void)name; 419 | (void)value; 420 | #if 0 421 | ALIAS_DEF a = jnlib_xmalloc( sizeof *a ); 422 | a->name = name; 423 | a->value = value; 424 | a->next = (ALIAS_DEF)arg->internal.aliases; 425 | (ALIAS_DEF)arg->internal.aliases = a; 426 | #endif 427 | } 428 | 429 | 430 | /* Return true if KEYWORD is in the ignore-invalid-option list. */ 431 | static int 432 | ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) 433 | { 434 | IIO_ITEM_DEF item = arg->internal.iio_list; 435 | 436 | for (; item; item = item->next) 437 | if (!strcmp (item->name, keyword)) 438 | return 1; 439 | return 0; 440 | } 441 | 442 | 443 | /* Add the keywords up to the next LF to the list of to be ignored 444 | options. After returning FP will either be at EOF or the next 445 | character read wll be the first of a new line. The function 446 | returns 0 on success or true on malloc failure. */ 447 | static int 448 | ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) 449 | { 450 | IIO_ITEM_DEF item; 451 | int c; 452 | char name[100]; 453 | int namelen = 0; 454 | int ready = 0; 455 | enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS; 456 | 457 | while (!ready) 458 | { 459 | c = getc (fp); 460 | if (c == '\n') 461 | ready = 1; 462 | else if (c == EOF) 463 | { 464 | c = '\n'; 465 | ready = 1; 466 | } 467 | again: 468 | switch (state) 469 | { 470 | case skipWS: 471 | if (!isascii (c) || !isspace(c)) 472 | { 473 | namelen = 0; 474 | state = collectNAME; 475 | goto again; 476 | } 477 | break; 478 | 479 | case collectNAME: 480 | if (isspace (c)) 481 | { 482 | state = addNAME; 483 | goto again; 484 | } 485 | else if (namelen < DIM(name)-1) 486 | name[namelen++] = c; 487 | else /* Too long. */ 488 | state = skipNAME; 489 | break; 490 | 491 | case skipNAME: 492 | if (isspace (c)) 493 | { 494 | state = skipWS; 495 | goto again; 496 | } 497 | break; 498 | 499 | case addNAME: 500 | name[namelen] = 0; 501 | if (!ignore_invalid_option_p (arg, name)) 502 | { 503 | item = jnlib_malloc (sizeof *item + namelen); 504 | if (!item) 505 | return 1; 506 | strcpy (item->name, name); 507 | item->next = (IIO_ITEM_DEF)arg->internal.iio_list; 508 | arg->internal.iio_list = item; 509 | } 510 | state = skipWS; 511 | goto again; 512 | } 513 | } 514 | return 0; 515 | } 516 | 517 | 518 | /* Clear the entire ignore-invalid-option list. */ 519 | static void 520 | ignore_invalid_option_clear (ARGPARSE_ARGS *arg) 521 | { 522 | IIO_ITEM_DEF item, tmpitem; 523 | 524 | for (item = arg->internal.iio_list; item; item = tmpitem) 525 | { 526 | tmpitem = item->next; 527 | jnlib_free (item); 528 | } 529 | arg->internal.iio_list = NULL; 530 | } 531 | 532 | 533 | 534 | /**************** 535 | * Get options from a file. 536 | * Lines starting with '#' are comment lines. 537 | * Syntax is simply a keyword and the argument. 538 | * Valid keywords are all keywords from the long_opt list without 539 | * the leading dashes. The special keywords "help", "warranty" and "version" 540 | * are not valid here. 541 | * The special keyword "alias" may be used to store alias definitions, 542 | * which are later expanded like long options. 543 | * The option 544 | * ignore-invalid-option OPTIONNAMEs 545 | * is recognized and updates a list of option which should be ignored if they 546 | * are not defined. 547 | * Caller must free returned strings. 548 | * If called with FP set to NULL command line args are parse instead. 549 | * 550 | * Q: Should we allow the syntax 551 | * keyword = value 552 | * and accept for boolean options a value of 1/0, yes/no or true/false? 553 | * Note: Abbreviation of options is here not allowed. 554 | */ 555 | int 556 | optfile_parse (FILE *fp, const char *filename, unsigned *lineno, 557 | ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) 558 | { 559 | int state, i, c; 560 | int idx=0; 561 | char keyword[100]; 562 | char *buffer = NULL; 563 | size_t buflen = 0; 564 | int in_alias=0; 565 | 566 | if (!fp) /* Divert to to arg_parse() in this case. */ 567 | return arg_parse (arg, opts); 568 | 569 | initialize (arg, filename, lineno); 570 | 571 | /* Find the next keyword. */ 572 | state = i = 0; 573 | for (;;) 574 | { 575 | c = getc (fp); 576 | if (c == '\n' || c== EOF ) 577 | { 578 | if ( c != EOF ) 579 | ++*lineno; 580 | if (state == -1) 581 | break; 582 | else if (state == 2) 583 | { 584 | keyword[i] = 0; 585 | for (i=0; opts[i].short_opt; i++ ) 586 | { 587 | if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) 588 | break; 589 | } 590 | idx = i; 591 | arg->r_opt = opts[idx].short_opt; 592 | if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) 593 | { 594 | state = i = 0; 595 | continue; 596 | } 597 | else if (!opts[idx].short_opt ) 598 | { 599 | if (!strcmp (keyword, "ignore-invalid-option")) 600 | { 601 | /* No argument - ignore this meta option. */ 602 | state = i = 0; 603 | continue; 604 | } 605 | else if (ignore_invalid_option_p (arg, keyword)) 606 | { 607 | /* This invalid option is in the iio list. */ 608 | state = i = 0; 609 | continue; 610 | } 611 | arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) 612 | ? ARGPARSE_INVALID_COMMAND 613 | : ARGPARSE_INVALID_OPTION); 614 | } 615 | else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) 616 | arg->r_type = 0; /* Does not take an arg. */ 617 | else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) ) 618 | arg->r_type = 0; /* Arg is optional. */ 619 | else 620 | arg->r_opt = ARGPARSE_MISSING_ARG; 621 | 622 | break; 623 | } 624 | else if (state == 3) 625 | { 626 | /* No argument found. */ 627 | if (in_alias) 628 | arg->r_opt = ARGPARSE_MISSING_ARG; 629 | else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) 630 | arg->r_type = 0; /* Does not take an arg. */ 631 | else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL)) 632 | arg->r_type = 0; /* No optional argument. */ 633 | else 634 | arg->r_opt = ARGPARSE_MISSING_ARG; 635 | 636 | break; 637 | } 638 | else if (state == 4) 639 | { 640 | /* Has an argument. */ 641 | if (in_alias) 642 | { 643 | if (!buffer) 644 | arg->r_opt = ARGPARSE_UNEXPECTED_ARG; 645 | else 646 | { 647 | char *p; 648 | 649 | buffer[i] = 0; 650 | p = strpbrk (buffer, " \t"); 651 | if (p) 652 | { 653 | *p++ = 0; 654 | trim_spaces (p); 655 | } 656 | if (!p || !*p) 657 | { 658 | jnlib_free (buffer); 659 | arg->r_opt = ARGPARSE_INVALID_ALIAS; 660 | } 661 | else 662 | { 663 | store_alias (arg, buffer, p); 664 | } 665 | } 666 | } 667 | else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) 668 | arg->r_opt = ARGPARSE_UNEXPECTED_ARG; 669 | else 670 | { 671 | char *p; 672 | 673 | if (!buffer) 674 | { 675 | keyword[i] = 0; 676 | buffer = jnlib_strdup (keyword); 677 | if (!buffer) 678 | arg->r_opt = ARGPARSE_OUT_OF_CORE; 679 | } 680 | else 681 | buffer[i] = 0; 682 | 683 | if (buffer) 684 | { 685 | trim_spaces (buffer); 686 | p = buffer; 687 | if (*p == '"') 688 | { 689 | /* Remove quotes. */ 690 | p++; 691 | if (*p && p[strlen(p)-1] == '\"' ) 692 | p[strlen(p)-1] = 0; 693 | } 694 | if (!set_opt_arg (arg, opts[idx].flags, p)) 695 | jnlib_free(buffer); 696 | } 697 | } 698 | break; 699 | } 700 | else if (c == EOF) 701 | { 702 | ignore_invalid_option_clear (arg); 703 | if (ferror (fp)) 704 | arg->r_opt = ARGPARSE_READ_ERROR; 705 | else 706 | arg->r_opt = 0; /* EOF. */ 707 | break; 708 | } 709 | state = 0; 710 | i = 0; 711 | } 712 | else if (state == -1) 713 | ; /* Skip. */ 714 | else if (state == 0 && isascii (c) && isspace(c)) 715 | ; /* Skip leading white space. */ 716 | else if (state == 0 && c == '#' ) 717 | state = 1; /* Start of a comment. */ 718 | else if (state == 1) 719 | ; /* Skip comments. */ 720 | else if (state == 2 && isascii (c) && isspace(c)) 721 | { 722 | /* Check keyword. */ 723 | keyword[i] = 0; 724 | for (i=0; opts[i].short_opt; i++ ) 725 | if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) 726 | break; 727 | idx = i; 728 | arg->r_opt = opts[idx].short_opt; 729 | if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) 730 | { 731 | state = 1; /* Process like a comment. */ 732 | } 733 | else if (!opts[idx].short_opt) 734 | { 735 | if (!strcmp (keyword, "alias")) 736 | { 737 | in_alias = 1; 738 | state = 3; 739 | } 740 | else if (!strcmp (keyword, "ignore-invalid-option")) 741 | { 742 | if (ignore_invalid_option_add (arg, fp)) 743 | { 744 | arg->r_opt = ARGPARSE_OUT_OF_CORE; 745 | break; 746 | } 747 | state = i = 0; 748 | ++*lineno; 749 | } 750 | else if (ignore_invalid_option_p (arg, keyword)) 751 | state = 1; /* Process like a comment. */ 752 | else 753 | { 754 | arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) 755 | ? ARGPARSE_INVALID_COMMAND 756 | : ARGPARSE_INVALID_OPTION); 757 | state = -1; /* Skip rest of line and leave. */ 758 | } 759 | } 760 | else 761 | state = 3; 762 | } 763 | else if (state == 3) 764 | { 765 | /* Skip leading spaces of the argument. */ 766 | if (!isascii (c) || !isspace(c)) 767 | { 768 | i = 0; 769 | keyword[i++] = c; 770 | state = 4; 771 | } 772 | } 773 | else if (state == 4) 774 | { 775 | /* Collect the argument. */ 776 | if (buffer) 777 | { 778 | if (i < buflen-1) 779 | buffer[i++] = c; 780 | else 781 | { 782 | char *tmp; 783 | size_t tmplen = buflen + 50; 784 | 785 | tmp = jnlib_realloc (buffer, tmplen); 786 | if (tmp) 787 | { 788 | buflen = tmplen; 789 | buffer = tmp; 790 | buffer[i++] = c; 791 | } 792 | else 793 | { 794 | jnlib_free (buffer); 795 | arg->r_opt = ARGPARSE_OUT_OF_CORE; 796 | break; 797 | } 798 | } 799 | } 800 | else if (i < DIM(keyword)-1) 801 | keyword[i++] = c; 802 | else 803 | { 804 | size_t tmplen = DIM(keyword) + 50; 805 | buffer = jnlib_malloc (tmplen); 806 | if (buffer) 807 | { 808 | buflen = tmplen; 809 | memcpy(buffer, keyword, i); 810 | buffer[i++] = c; 811 | } 812 | else 813 | { 814 | arg->r_opt = ARGPARSE_OUT_OF_CORE; 815 | break; 816 | } 817 | } 818 | } 819 | else if (i >= DIM(keyword)-1) 820 | { 821 | arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG; 822 | state = -1; /* Skip rest of line and leave. */ 823 | } 824 | else 825 | { 826 | keyword[i++] = c; 827 | state = 2; 828 | } 829 | } 830 | 831 | return arg->r_opt; 832 | } 833 | 834 | 835 | 836 | static int 837 | find_long_option( ARGPARSE_ARGS *arg, 838 | ARGPARSE_OPTS *opts, const char *keyword ) 839 | { 840 | int i; 841 | size_t n; 842 | 843 | (void)arg; 844 | 845 | /* Would be better if we can do a binary search, but it is not 846 | possible to reorder our option table because we would mess 847 | up our help strings - What we can do is: Build a nice option 848 | lookup table wehn this function is first invoked */ 849 | if( !*keyword ) 850 | return -1; 851 | for(i=0; opts[i].short_opt; i++ ) 852 | if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) ) 853 | return i; 854 | #if 0 855 | { 856 | ALIAS_DEF a; 857 | /* see whether it is an alias */ 858 | for( a = args->internal.aliases; a; a = a->next ) { 859 | if( !strcmp( a->name, keyword) ) { 860 | /* todo: must parse the alias here */ 861 | args->internal.cur_alias = a; 862 | return -3; /* alias available */ 863 | } 864 | } 865 | } 866 | #endif 867 | /* not found, see whether it is an abbreviation */ 868 | /* aliases may not be abbreviated */ 869 | n = strlen( keyword ); 870 | for(i=0; opts[i].short_opt; i++ ) { 871 | if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) { 872 | int j; 873 | for(j=i+1; opts[j].short_opt; j++ ) { 874 | if( opts[j].long_opt 875 | && !strncmp( opts[j].long_opt, keyword, n ) ) 876 | return -2; /* abbreviation is ambiguous */ 877 | } 878 | return i; 879 | } 880 | } 881 | return -1; /* Not found. */ 882 | } 883 | 884 | int 885 | arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) 886 | { 887 | int idx; 888 | int argc; 889 | char **argv; 890 | char *s, *s2; 891 | int i; 892 | 893 | initialize( arg, NULL, NULL ); 894 | argc = *arg->argc; 895 | argv = *arg->argv; 896 | idx = arg->internal.idx; 897 | 898 | if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0)) 899 | { 900 | /* Skip the first argument. */ 901 | argc--; argv++; idx++; 902 | } 903 | 904 | next_one: 905 | if (!argc) 906 | { 907 | /* No more args. */ 908 | arg->r_opt = 0; 909 | goto leave; /* Ready. */ 910 | } 911 | 912 | s = *argv; 913 | arg->internal.last = s; 914 | 915 | if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL)) 916 | { 917 | arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */ 918 | arg->r_type = 2; 919 | arg->r.ret_str = s; 920 | argc--; argv++; idx++; /* set to next one */ 921 | } 922 | else if( arg->internal.stopped ) 923 | { 924 | arg->r_opt = 0; 925 | goto leave; /* Ready. */ 926 | } 927 | else if ( *s == '-' && s[1] == '-' ) 928 | { 929 | /* Long option. */ 930 | char *argpos; 931 | 932 | arg->internal.inarg = 0; 933 | if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP)) 934 | { 935 | /* Stop option processing. */ 936 | arg->internal.stopped = 1; 937 | arg->flags |= ARGPARSE_FLAG_STOP_SEEN; 938 | argc--; argv++; idx++; 939 | goto next_one; 940 | } 941 | 942 | argpos = strchr( s+2, '=' ); 943 | if ( argpos ) 944 | *argpos = 0; 945 | i = find_long_option ( arg, opts, s+2 ); 946 | if ( argpos ) 947 | *argpos = '='; 948 | 949 | if ( i < 0 && !strcmp ( "help", s+2) ) 950 | show_help (opts, arg->flags); 951 | else if ( i < 0 && !strcmp ( "version", s+2) ) 952 | { 953 | if (!(arg->flags & ARGPARSE_FLAG_NOVERSION)) 954 | { 955 | show_version (); 956 | exit(0); 957 | } 958 | } 959 | else if ( i < 0 && !strcmp( "warranty", s+2)) 960 | { 961 | writestrings (0, strusage (16), "\n", NULL); 962 | exit (0); 963 | } 964 | else if ( i < 0 && !strcmp( "dump-options", s+2) ) 965 | { 966 | for (i=0; opts[i].short_opt; i++ ) 967 | { 968 | if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE)) 969 | writestrings (0, "--", opts[i].long_opt, "\n", NULL); 970 | } 971 | writestrings (0, "--dump-options\n--help\n--version\n--warranty\n", 972 | NULL); 973 | exit (0); 974 | } 975 | 976 | if ( i == -2 ) 977 | arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION; 978 | else if ( i == -1 ) 979 | { 980 | arg->r_opt = ARGPARSE_INVALID_OPTION; 981 | arg->r.ret_str = s+2; 982 | } 983 | else 984 | arg->r_opt = opts[i].short_opt; 985 | if ( i < 0 ) 986 | ; 987 | else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) 988 | { 989 | if ( argpos ) 990 | { 991 | s2 = argpos+1; 992 | if ( !*s2 ) 993 | s2 = NULL; 994 | } 995 | else 996 | s2 = argv[1]; 997 | if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) 998 | { 999 | arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */ 1000 | } 1001 | else if ( !s2 ) 1002 | { 1003 | arg->r_opt = ARGPARSE_MISSING_ARG; 1004 | } 1005 | else if ( !argpos && *s2 == '-' 1006 | && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) 1007 | { 1008 | /* The argument is optional and the next seems to be an 1009 | option. We do not check this possible option but 1010 | assume no argument */ 1011 | arg->r_type = ARGPARSE_TYPE_NONE; 1012 | } 1013 | else 1014 | { 1015 | set_opt_arg (arg, opts[i].flags, s2); 1016 | if ( !argpos ) 1017 | { 1018 | argc--; argv++; idx++; /* Skip one. */ 1019 | } 1020 | } 1021 | } 1022 | else 1023 | { 1024 | /* Does not take an argument. */ 1025 | if ( argpos ) 1026 | arg->r_type = ARGPARSE_UNEXPECTED_ARG; 1027 | else 1028 | arg->r_type = 0; 1029 | } 1030 | argc--; argv++; idx++; /* Set to next one. */ 1031 | } 1032 | else if ( (*s == '-' && s[1]) || arg->internal.inarg ) 1033 | { 1034 | /* Short option. */ 1035 | int dash_kludge = 0; 1036 | 1037 | i = 0; 1038 | if ( !arg->internal.inarg ) 1039 | { 1040 | arg->internal.inarg++; 1041 | if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) ) 1042 | { 1043 | for (i=0; opts[i].short_opt; i++ ) 1044 | if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1)) 1045 | { 1046 | dash_kludge = 1; 1047 | break; 1048 | } 1049 | } 1050 | } 1051 | s += arg->internal.inarg; 1052 | 1053 | if (!dash_kludge ) 1054 | { 1055 | for (i=0; opts[i].short_opt; i++ ) 1056 | if ( opts[i].short_opt == *s ) 1057 | break; 1058 | } 1059 | 1060 | if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) 1061 | show_help (opts, arg->flags); 1062 | 1063 | arg->r_opt = opts[i].short_opt; 1064 | if (!opts[i].short_opt ) 1065 | { 1066 | arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)? 1067 | ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION; 1068 | arg->internal.inarg++; /* Point to the next arg. */ 1069 | arg->r.ret_str = s; 1070 | } 1071 | else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) 1072 | { 1073 | if ( s[1] && !dash_kludge ) 1074 | { 1075 | s2 = s+1; 1076 | set_opt_arg (arg, opts[i].flags, s2); 1077 | } 1078 | else 1079 | { 1080 | s2 = argv[1]; 1081 | if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) 1082 | { 1083 | arg->r_type = ARGPARSE_TYPE_NONE; 1084 | } 1085 | else if ( !s2 ) 1086 | { 1087 | arg->r_opt = ARGPARSE_MISSING_ARG; 1088 | } 1089 | else if ( *s2 == '-' && s2[1] 1090 | && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) 1091 | { 1092 | /* The argument is optional and the next seems to 1093 | be an option. We do not check this possible 1094 | option but assume no argument. */ 1095 | arg->r_type = ARGPARSE_TYPE_NONE; 1096 | } 1097 | else 1098 | { 1099 | set_opt_arg (arg, opts[i].flags, s2); 1100 | argc--; argv++; idx++; /* Skip one. */ 1101 | } 1102 | } 1103 | s = "x"; /* This is so that !s[1] yields false. */ 1104 | } 1105 | else 1106 | { 1107 | /* Does not take an argument. */ 1108 | arg->r_type = ARGPARSE_TYPE_NONE; 1109 | arg->internal.inarg++; /* Point to the next arg. */ 1110 | } 1111 | if ( !s[1] || dash_kludge ) 1112 | { 1113 | /* No more concatenated short options. */ 1114 | arg->internal.inarg = 0; 1115 | argc--; argv++; idx++; 1116 | } 1117 | } 1118 | else if ( arg->flags & ARGPARSE_FLAG_MIXED ) 1119 | { 1120 | arg->r_opt = ARGPARSE_IS_ARG; 1121 | arg->r_type = 2; 1122 | arg->r.ret_str = s; 1123 | argc--; argv++; idx++; /* Set to next one. */ 1124 | } 1125 | else 1126 | { 1127 | arg->internal.stopped = 1; /* Stop option processing. */ 1128 | goto next_one; 1129 | } 1130 | 1131 | leave: 1132 | *arg->argc = argc; 1133 | *arg->argv = argv; 1134 | arg->internal.idx = idx; 1135 | return arg->r_opt; 1136 | } 1137 | 1138 | 1139 | /* Returns: -1 on error, 0 for an integer type and 1 for a non integer 1140 | type argument. */ 1141 | static int 1142 | set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s) 1143 | { 1144 | int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10; 1145 | long l; 1146 | 1147 | switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) ) 1148 | { 1149 | case ARGPARSE_TYPE_LONG: 1150 | case ARGPARSE_TYPE_INT: 1151 | errno = 0; 1152 | l = strtol (s, NULL, base); 1153 | if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) 1154 | { 1155 | arg->r_opt = ARGPARSE_INVALID_ARG; 1156 | return -1; 1157 | } 1158 | if (arg->r_type == ARGPARSE_TYPE_LONG) 1159 | arg->r.ret_long = l; 1160 | else if ( (l < 0 && l < INT_MIN) || l > INT_MAX ) 1161 | { 1162 | arg->r_opt = ARGPARSE_INVALID_ARG; 1163 | return -1; 1164 | } 1165 | else 1166 | arg->r.ret_int = (int)l; 1167 | return 0; 1168 | 1169 | case ARGPARSE_TYPE_ULONG: 1170 | while (isascii (*s) && isspace(*s)) 1171 | s++; 1172 | if (*s == '-') 1173 | { 1174 | arg->r.ret_ulong = 0; 1175 | arg->r_opt = ARGPARSE_INVALID_ARG; 1176 | return -1; 1177 | } 1178 | errno = 0; 1179 | arg->r.ret_ulong = strtoul (s, NULL, base); 1180 | if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE) 1181 | { 1182 | arg->r_opt = ARGPARSE_INVALID_ARG; 1183 | return -1; 1184 | } 1185 | return 0; 1186 | 1187 | case ARGPARSE_TYPE_STRING: 1188 | default: 1189 | arg->r.ret_str = s; 1190 | return 1; 1191 | } 1192 | } 1193 | 1194 | 1195 | static size_t 1196 | long_opt_strlen( ARGPARSE_OPTS *o ) 1197 | { 1198 | size_t n = strlen (o->long_opt); 1199 | 1200 | if ( o->description && *o->description == '|' ) 1201 | { 1202 | const char *s; 1203 | #ifdef JNLIB_NEED_UTF8CONV 1204 | int is_utf8 = is_native_utf8 (); 1205 | #endif 1206 | 1207 | s=o->description+1; 1208 | if ( *s != '=' ) 1209 | n++; 1210 | /* For a (mostly) correct length calculation we exclude 1211 | continuation bytes (10xxxxxx) if we are on a native utf8 1212 | terminal. */ 1213 | for (; *s && *s != '|'; s++ ) 1214 | #ifdef JNLIB_NEED_UTF8CONV 1215 | if ( is_utf8 && (*s&0xc0) != 0x80 ) 1216 | #endif 1217 | n++; 1218 | } 1219 | return n; 1220 | } 1221 | 1222 | 1223 | /**************** 1224 | * Print formatted help. The description string has some special 1225 | * meanings: 1226 | * - A description string which is "@" suppresses help output for 1227 | * this option 1228 | * - a description,ine which starts with a '@' and is followed by 1229 | * any other characters is printed as is; this may be used for examples 1230 | * ans such. 1231 | * - A description which starts with a '|' outputs the string between this 1232 | * bar and the next one as arguments of the long option. 1233 | */ 1234 | static void 1235 | show_help (ARGPARSE_OPTS *opts, unsigned int flags) 1236 | { 1237 | const char *s; 1238 | char tmp[2]; 1239 | 1240 | show_version (); 1241 | writestrings (0, "\n", NULL); 1242 | s = strusage (42); 1243 | if (s && *s == '1') 1244 | { 1245 | s = strusage (40); 1246 | writestrings (1, s, NULL); 1247 | if (*s && s[strlen(s)] != '\n') 1248 | writestrings (1, "\n", NULL); 1249 | } 1250 | s = strusage(41); 1251 | writestrings (0, s, "\n", NULL); 1252 | if ( opts[0].description ) 1253 | { 1254 | /* Auto format the option description. */ 1255 | int i,j, indent; 1256 | 1257 | /* Get max. length of long options. */ 1258 | for (i=indent=0; opts[i].short_opt; i++ ) 1259 | { 1260 | if ( opts[i].long_opt ) 1261 | if ( !opts[i].description || *opts[i].description != '@' ) 1262 | if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 ) 1263 | indent = j; 1264 | } 1265 | 1266 | /* Example: " -v, --verbose Viele Sachen ausgeben" */ 1267 | indent += 10; 1268 | if ( *opts[0].description != '@' ) 1269 | writestrings (0, "Options:", "\n", NULL); 1270 | for (i=0; opts[i].short_opt; i++ ) 1271 | { 1272 | s = map_static_macro_string (_( opts[i].description )); 1273 | if ( s && *s== '@' && !s[1] ) /* Hide this line. */ 1274 | continue; 1275 | if ( s && *s == '@' ) /* Unindented comment only line. */ 1276 | { 1277 | for (s++; *s; s++ ) 1278 | { 1279 | if ( *s == '\n' ) 1280 | { 1281 | if( s[1] ) 1282 | writestrings (0, "\n", NULL); 1283 | } 1284 | else 1285 | { 1286 | tmp[0] = *s; 1287 | tmp[1] = 0; 1288 | writestrings (0, tmp, NULL); 1289 | } 1290 | } 1291 | writestrings (0, "\n", NULL); 1292 | continue; 1293 | } 1294 | 1295 | j = 3; 1296 | if ( opts[i].short_opt < 256 ) 1297 | { 1298 | tmp[0] = opts[i].short_opt; 1299 | tmp[1] = 0; 1300 | writestrings (0, " -", tmp, NULL ); 1301 | if ( !opts[i].long_opt ) 1302 | { 1303 | if (s && *s == '|' ) 1304 | { 1305 | writestrings (0, " ", NULL); j++; 1306 | for (s++ ; *s && *s != '|'; s++, j++ ) 1307 | { 1308 | tmp[0] = *s; 1309 | tmp[1] = 0; 1310 | writestrings (0, tmp, NULL); 1311 | } 1312 | if ( *s ) 1313 | s++; 1314 | } 1315 | } 1316 | } 1317 | else 1318 | writestrings (0, " ", NULL); 1319 | if ( opts[i].long_opt ) 1320 | { 1321 | tmp[0] = opts[i].short_opt < 256?',':' '; 1322 | tmp[1] = 0; 1323 | j += writestrings (0, tmp, " --", opts[i].long_opt, NULL); 1324 | if (s && *s == '|' ) 1325 | { 1326 | if ( *++s != '=' ) 1327 | { 1328 | writestrings (0, " ", NULL); 1329 | j++; 1330 | } 1331 | for ( ; *s && *s != '|'; s++, j++ ) 1332 | { 1333 | tmp[0] = *s; 1334 | tmp[1] = 0; 1335 | writestrings (0, tmp, NULL); 1336 | } 1337 | if ( *s ) 1338 | s++; 1339 | } 1340 | writestrings (0, " ", NULL); 1341 | j += 3; 1342 | } 1343 | for (;j < indent; j++ ) 1344 | writestrings (0, " ", NULL); 1345 | if ( s ) 1346 | { 1347 | if ( *s && j > indent ) 1348 | { 1349 | writestrings (0, "\n", NULL); 1350 | for (j=0;j < indent; j++ ) 1351 | writestrings (0, " ", NULL); 1352 | } 1353 | for (; *s; s++ ) 1354 | { 1355 | if ( *s == '\n' ) 1356 | { 1357 | if ( s[1] ) 1358 | { 1359 | writestrings (0, "\n", NULL); 1360 | for (j=0; j < indent; j++ ) 1361 | writestrings (0, " ", NULL); 1362 | } 1363 | } 1364 | else 1365 | { 1366 | tmp[0] = *s; 1367 | tmp[1] = 0; 1368 | writestrings (0, tmp, NULL); 1369 | } 1370 | } 1371 | } 1372 | writestrings (0, "\n", NULL); 1373 | } 1374 | if ( (flags & ARGPARSE_FLAG_ONEDASH) ) 1375 | writestrings (0, "\n(A single dash may be used " 1376 | "instead of the double ones)\n", NULL); 1377 | } 1378 | if ( (s=strusage(19)) ) 1379 | { 1380 | writestrings (0, "\n", NULL); 1381 | writestrings (0, s, NULL); 1382 | } 1383 | flushstrings (0); 1384 | exit(0); 1385 | } 1386 | 1387 | static void 1388 | show_version () 1389 | { 1390 | const char *s; 1391 | int i; 1392 | 1393 | /* Version line. */ 1394 | writestrings (0, strusage (11), NULL); 1395 | if ((s=strusage (12))) 1396 | writestrings (0, " (", s, ")", NULL); 1397 | writestrings (0, " ", strusage (13), "\n", NULL); 1398 | /* Additional version lines. */ 1399 | for (i=20; i < 30; i++) 1400 | if ((s=strusage (i))) 1401 | writestrings (0, s, "\n", NULL); 1402 | /* Copyright string. */ 1403 | if ((s=strusage (14))) 1404 | writestrings (0, s, "\n", NULL); 1405 | /* Licence string. */ 1406 | if( (s=strusage (10)) ) 1407 | writestrings (0, s, "\n", NULL); 1408 | /* Copying conditions. */ 1409 | if ( (s=strusage(15)) ) 1410 | writestrings (0, s, NULL); 1411 | /* Thanks. */ 1412 | if ((s=strusage(18))) 1413 | writestrings (0, s, NULL); 1414 | /* Additional program info. */ 1415 | for (i=30; i < 40; i++ ) 1416 | if ( (s=strusage (i)) ) 1417 | writestrings (0, s, NULL); 1418 | flushstrings (0); 1419 | } 1420 | 1421 | 1422 | void 1423 | usage (int level) 1424 | { 1425 | const char *p; 1426 | 1427 | if (!level) 1428 | { 1429 | writestrings (1, strusage(11), " ", strusage(13), "; ", 1430 | strusage (14), "\n", NULL); 1431 | flushstrings (1); 1432 | } 1433 | else if (level == 1) 1434 | { 1435 | p = strusage (40); 1436 | writestrings (1, p, NULL); 1437 | if (*p && p[strlen(p)] != '\n') 1438 | writestrings (1, "\n", NULL); 1439 | exit (2); 1440 | } 1441 | else if (level == 2) 1442 | { 1443 | p = strusage (42); 1444 | if (p && *p == '1') 1445 | { 1446 | p = strusage (40); 1447 | writestrings (1, p, NULL); 1448 | if (*p && p[strlen(p)] != '\n') 1449 | writestrings (1, "\n", NULL); 1450 | } 1451 | writestrings (0, strusage(41), "\n", NULL); 1452 | exit (0); 1453 | } 1454 | } 1455 | 1456 | /* Level 1457 | * 0: Print copyright string to stderr 1458 | * 1: Print a short usage hint to stderr and terminate 1459 | * 2: Print a long usage hint to stdout and terminate 1460 | * 10: Return license info string 1461 | * 11: Return the name of the program 1462 | * 12: Return optional name of package which includes this program. 1463 | * 13: version string 1464 | * 14: copyright string 1465 | * 15: Short copying conditions (with LFs) 1466 | * 16: Long copying conditions (with LFs) 1467 | * 17: Optional printable OS name 1468 | * 18: Optional thanks list (with LFs) 1469 | * 19: Bug report info 1470 | *20..29: Additional lib version strings. 1471 | *30..39: Additional program info (with LFs) 1472 | * 40: short usage note (with LF) 1473 | * 41: long usage note (with LF) 1474 | * 42: Flag string: 1475 | * First char is '1': 1476 | * The short usage notes needs to be printed 1477 | * before the long usage note. 1478 | */ 1479 | const char * 1480 | strusage( int level ) 1481 | { 1482 | const char *p = strusage_handler? strusage_handler(level) : NULL; 1483 | 1484 | if ( p ) 1485 | return map_static_macro_string (p); 1486 | 1487 | switch ( level ) 1488 | { 1489 | 1490 | case 10: 1491 | #if ARGPARSE_GPL_VERSION == 3 1492 | p = ("License GPLv3+: GNU GPL version 3 or later " 1493 | ""); 1494 | #else 1495 | p = ("License GPLv2+: GNU GPL version 2 or later " 1496 | ""); 1497 | #endif 1498 | break; 1499 | case 11: p = "foo"; break; 1500 | case 13: p = "0.0"; break; 1501 | case 14: p = ARGPARSE_CRIGHT_STR; break; 1502 | case 15: p = 1503 | "This is free software: you are free to change and redistribute it.\n" 1504 | "There is NO WARRANTY, to the extent permitted by law.\n"; 1505 | break; 1506 | case 16: p = 1507 | "This is free software; you can redistribute it and/or modify\n" 1508 | "it under the terms of the GNU General Public License as published by\n" 1509 | "the Free Software Foundation; either version " 1510 | ARGPARSE_STR2(ARGPARSE_GPL_VERSION) 1511 | " of the License, or\n" 1512 | "(at your option) any later version.\n\n" 1513 | "It is distributed in the hope that it will be useful,\n" 1514 | "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" 1515 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" 1516 | "GNU General Public License for more details.\n\n" 1517 | "You should have received a copy of the GNU General Public License\n" 1518 | "along with this software. If not, see .\n"; 1519 | break; 1520 | case 40: /* short and long usage */ 1521 | case 41: p = ""; break; 1522 | } 1523 | 1524 | return p; 1525 | } 1526 | 1527 | 1528 | /* Set the usage handler. This function is basically a constructor. */ 1529 | void 1530 | set_strusage ( const char *(*f)( int ) ) 1531 | { 1532 | strusage_handler = f; 1533 | } 1534 | 1535 | 1536 | #ifdef TEST 1537 | static struct { 1538 | int verbose; 1539 | int debug; 1540 | char *outfile; 1541 | char *crf; 1542 | int myopt; 1543 | int echo; 1544 | int a_long_one; 1545 | } opt; 1546 | 1547 | int 1548 | main(int argc, char **argv) 1549 | { 1550 | ARGPARSE_OPTS opts[] = { 1551 | ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"), 1552 | ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, " 1553 | "was wir eingegeben haben")), 1554 | ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"), 1555 | ARGPARSE_s_s('o', "output", 0 ), 1556 | ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ), 1557 | /* Note that on a non-utf8 terminal the ß might garble the output. */ 1558 | ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"), 1559 | ARGPARSE_o_i('m', "my-option", 0), 1560 | ARGPARSE_s_n(500, "a-long-option", 0 ), 1561 | ARGPARSE_end() 1562 | }; 1563 | ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL 1564 | | ARGPARSE_FLAG_MIXED 1565 | | ARGPARSE_FLAG_ONEDASH) }; 1566 | int i; 1567 | 1568 | while (arg_parse (&pargs, opts)) 1569 | { 1570 | switch (pargs.r_opt) 1571 | { 1572 | case ARGPARSE_IS_ARG : 1573 | printf ("arg='%s'\n", pargs.r.ret_str); 1574 | break; 1575 | case 'v': opt.verbose++; break; 1576 | case 'e': opt.echo++; break; 1577 | case 'd': opt.debug++; break; 1578 | case 'o': opt.outfile = pargs.r.ret_str; break; 1579 | case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; 1580 | case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; 1581 | case 500: opt.a_long_one++; break; 1582 | default : pargs.err = ARGPARSE_PRINT_WARNING; break; 1583 | } 1584 | } 1585 | for (i=0; i < argc; i++ ) 1586 | printf ("%3d -> (%s)\n", i, argv[i] ); 1587 | puts ("Options:"); 1588 | if (opt.verbose) 1589 | printf (" verbose=%d\n", opt.verbose ); 1590 | if (opt.debug) 1591 | printf (" debug=%d\n", opt.debug ); 1592 | if (opt.outfile) 1593 | printf (" outfile='%s'\n", opt.outfile ); 1594 | if (opt.crf) 1595 | printf (" crffile='%s'\n", opt.crf ); 1596 | if (opt.myopt) 1597 | printf (" myopt=%d\n", opt.myopt ); 1598 | if (opt.a_long_one) 1599 | printf (" a-long-one=%d\n", opt.a_long_one ); 1600 | if (opt.echo) 1601 | printf (" echo=%d\n", opt.echo ); 1602 | 1603 | return 0; 1604 | } 1605 | #endif /*TEST*/ 1606 | 1607 | /**** bottom of file ****/ 1608 | -------------------------------------------------------------------------------- /pinentry/argparse.h: -------------------------------------------------------------------------------- 1 | /* argparse.h - Argument parser for option handling. 2 | * Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc. 3 | * 4 | * This file is part of JNLIB, which is a subsystem of GnuPG. 5 | * 6 | * JNLIB is free software; you can redistribute it and/or modify it 7 | * under the terms of either 8 | * 9 | * - the GNU Lesser General Public License as published by the Free 10 | * Software Foundation; either version 3 of the License, or (at 11 | * your option) any later version. 12 | * 13 | * or 14 | * 15 | * - the GNU General Public License as published by the Free 16 | * Software Foundation; either version 2 of the License, or (at 17 | * your option) any later version. 18 | * 19 | * or both in parallel, as here. 20 | * 21 | * JNLIB is distributed in the hope that it will be useful, but 22 | * WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | * General Public License for more details. 25 | * 26 | * You should have received a copies of the GNU General Public License 27 | * and the GNU Lesser General Public License along with this program; 28 | * if not, see . 29 | */ 30 | 31 | #ifndef LIBJNLIB_ARGPARSE_H 32 | #define LIBJNLIB_ARGPARSE_H 33 | 34 | #include 35 | 36 | typedef struct 37 | { 38 | int *argc; /* Pointer to ARGC (value subject to change). */ 39 | char ***argv; /* Pointer to ARGV (value subject to change). */ 40 | unsigned int flags; /* Global flags. May be set prior to calling the 41 | parser. The parser may change the value. */ 42 | int err; /* Print error description for last option. 43 | Either 0, ARGPARSE_PRINT_WARNING or 44 | ARGPARSE_PRINT_ERROR. */ 45 | 46 | int r_opt; /* Returns option code. */ 47 | int r_type; /* Returns type of option value. */ 48 | union { 49 | int ret_int; 50 | long ret_long; 51 | unsigned long ret_ulong; 52 | char *ret_str; 53 | } r; /* Return values */ 54 | 55 | struct { 56 | int idx; 57 | int inarg; 58 | int stopped; 59 | const char *last; 60 | void *aliases; 61 | const void *cur_alias; 62 | void *iio_list; 63 | } internal; /* Private - do not change. */ 64 | } ARGPARSE_ARGS; 65 | 66 | typedef struct 67 | { 68 | int short_opt; 69 | const char *long_opt; 70 | unsigned int flags; 71 | const char *description; /* Optional option description. */ 72 | } ARGPARSE_OPTS; 73 | 74 | 75 | /* Global flags (ARGPARSE_ARGS). */ 76 | #define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ 77 | #define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return 78 | remaining args with R_OPT set to -1. */ 79 | #define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ 80 | #define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ 81 | #define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ 82 | #define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ 83 | #define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ 84 | 85 | #define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */ 86 | 87 | /* Flags for each option (ARGPARSE_OPTS). The type code may be 88 | ORed with the OPT flags. */ 89 | #define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ 90 | #define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ 91 | #define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ 92 | #define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ 93 | #define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ 94 | #define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ 95 | #define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ 96 | #define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */ 97 | #define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */ 98 | 99 | #define ARGPARSE_TYPE_MASK 7 /* Mask for the type values (internal). */ 100 | 101 | /* A set of macros to make option definitions easier to read. */ 102 | #define ARGPARSE_x(s,l,t,f,d) \ 103 | { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } 104 | 105 | #define ARGPARSE_s(s,l,t,d) \ 106 | { (s), (l), ARGPARSE_TYPE_ ## t, (d) } 107 | #define ARGPARSE_s_n(s,l,d) \ 108 | { (s), (l), ARGPARSE_TYPE_NONE, (d) } 109 | #define ARGPARSE_s_i(s,l,d) \ 110 | { (s), (l), ARGPARSE_TYPE_INT, (d) } 111 | #define ARGPARSE_s_s(s,l,d) \ 112 | { (s), (l), ARGPARSE_TYPE_STRING, (d) } 113 | #define ARGPARSE_s_l(s,l,d) \ 114 | { (s), (l), ARGPARSE_TYPE_LONG, (d) } 115 | #define ARGPARSE_s_u(s,l,d) \ 116 | { (s), (l), ARGPARSE_TYPE_ULONG, (d) } 117 | 118 | #define ARGPARSE_o(s,l,t,d) \ 119 | { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } 120 | #define ARGPARSE_o_n(s,l,d) \ 121 | { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } 122 | #define ARGPARSE_o_i(s,l,d) \ 123 | { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } 124 | #define ARGPARSE_o_s(s,l,d) \ 125 | { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } 126 | #define ARGPARSE_o_l(s,l,d) \ 127 | { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } 128 | #define ARGPARSE_o_u(s,l,d) \ 129 | { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } 130 | 131 | #define ARGPARSE_p(s,l,t,d) \ 132 | { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } 133 | #define ARGPARSE_p_n(s,l,d) \ 134 | { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } 135 | #define ARGPARSE_p_i(s,l,d) \ 136 | { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } 137 | #define ARGPARSE_p_s(s,l,d) \ 138 | { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } 139 | #define ARGPARSE_p_l(s,l,d) \ 140 | { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } 141 | #define ARGPARSE_p_u(s,l,d) \ 142 | { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } 143 | 144 | #define ARGPARSE_op(s,l,t,d) \ 145 | { (s), (l), (ARGPARSE_TYPE_ ## t \ 146 | | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } 147 | #define ARGPARSE_op_n(s,l,d) \ 148 | { (s), (l), (ARGPARSE_TYPE_NONE \ 149 | | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } 150 | #define ARGPARSE_op_i(s,l,d) \ 151 | { (s), (l), (ARGPARSE_TYPE_INT \ 152 | | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } 153 | #define ARGPARSE_op_s(s,l,d) \ 154 | { (s), (l), (ARGPARSE_TYPE_STRING \ 155 | | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } 156 | #define ARGPARSE_op_l(s,l,d) \ 157 | { (s), (l), (ARGPARSE_TYPE_LONG \ 158 | | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } 159 | #define ARGPARSE_op_u(s,l,d) \ 160 | { (s), (l), (ARGPARSE_TYPE_ULONG \ 161 | | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } 162 | 163 | #define ARGPARSE_c(s,l,d) \ 164 | { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } 165 | 166 | #define ARGPARSE_ignore(s,l) \ 167 | { (s), (l), (ARGPARSE_OPT_IGNORE), "@" } 168 | 169 | #define ARGPARSE_group(s,d) \ 170 | { (s), NULL, 0, (d) } 171 | 172 | #define ARGPARSE_end() { 0, NULL, 0, NULL } 173 | 174 | 175 | /* Other constants. */ 176 | #define ARGPARSE_PRINT_WARNING 1 177 | #define ARGPARSE_PRINT_ERROR 2 178 | 179 | 180 | /* Error values. */ 181 | #define ARGPARSE_IS_ARG (-1) 182 | #define ARGPARSE_INVALID_OPTION (-2) 183 | #define ARGPARSE_MISSING_ARG (-3) 184 | #define ARGPARSE_KEYWORD_TOO_LONG (-4) 185 | #define ARGPARSE_READ_ERROR (-5) 186 | #define ARGPARSE_UNEXPECTED_ARG (-6) 187 | #define ARGPARSE_INVALID_COMMAND (-7) 188 | #define ARGPARSE_AMBIGUOUS_OPTION (-8) 189 | #define ARGPARSE_AMBIGUOUS_COMMAND (-9) 190 | #define ARGPARSE_INVALID_ALIAS (-10) 191 | #define ARGPARSE_OUT_OF_CORE (-11) 192 | #define ARGPARSE_INVALID_ARG (-12) 193 | 194 | 195 | int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); 196 | int optfile_parse (FILE *fp, const char *filename, unsigned *lineno, 197 | ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); 198 | void usage (int level); 199 | const char *strusage (int level); 200 | void set_strusage (const char *(*f)( int )); 201 | void argparse_register_outfnc (int (*fnc)(int, const char *)); 202 | 203 | #endif /*LIBJNLIB_ARGPARSE_H*/ 204 | -------------------------------------------------------------------------------- /pinentry/memory.h: -------------------------------------------------------------------------------- 1 | /* Quintuple Agent secure memory allocation 2 | * Copyright (C) 1998,1999 Free Software Foundation, Inc. 3 | * Copyright (C) 1999,2000 Robert Bihlmeyer 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #ifndef _MEMORY_H 21 | #define _MEMORY_H 22 | 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #if 0 28 | } 29 | #endif 30 | #endif 31 | 32 | 33 | /* values for flags, hardcoded in secmem.c */ 34 | #define SECMEM_WARN 0 35 | #define SECMEM_DONT_WARN 1 36 | #define SECMEM_SUSPEND_WARN 2 37 | 38 | void secmem_init( size_t npool ); 39 | void secmem_term( void ); 40 | void *secmem_malloc( size_t size ); 41 | void *secmem_realloc( void *a, size_t newsize ); 42 | void secmem_free( void *a ); 43 | int m_is_secure( const void *p ); 44 | void secmem_dump_stats(void); 45 | void secmem_set_flags( unsigned flags ); 46 | unsigned secmem_get_flags(void); 47 | size_t secmem_get_max_size (void); 48 | 49 | #if 0 50 | { 51 | #endif 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | #endif /* _MEMORY_H */ 56 | -------------------------------------------------------------------------------- /pinentry/password-cache.c: -------------------------------------------------------------------------------- 1 | /* password-cache.c - Password cache support. 2 | Copyright (C) 2015 g10 Code GmbH 3 | 4 | This file is part of PINENTRY. 5 | 6 | PINENTRY is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | PINENTRY is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, see . 18 | */ 19 | 20 | #ifdef HAVE_CONFIG_H 21 | # include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef HAVE_LIBSECRET 29 | # include 30 | #endif 31 | 32 | #include "password-cache.h" 33 | #include "memory.h" 34 | 35 | #ifdef HAVE_LIBSECRET 36 | static const SecretSchema * 37 | gpg_schema (void) 38 | { 39 | static const SecretSchema the_schema = { 40 | "org.gnupg.Passphrase", SECRET_SCHEMA_NONE, 41 | { 42 | { "stored-by", SECRET_SCHEMA_ATTRIBUTE_STRING }, 43 | { "keygrip", SECRET_SCHEMA_ATTRIBUTE_STRING }, 44 | { "NULL", 0 }, 45 | } 46 | }; 47 | return &the_schema; 48 | } 49 | 50 | static char * 51 | keygrip_to_label (const char *keygrip) 52 | { 53 | char const prefix[] = "GnuPG: "; 54 | char *label; 55 | 56 | label = malloc (sizeof (prefix) + strlen (keygrip)); 57 | if (label) 58 | { 59 | memcpy (label, prefix, sizeof (prefix) - 1); 60 | strcpy (&label[sizeof (prefix) - 1], keygrip); 61 | } 62 | return label; 63 | } 64 | #endif 65 | 66 | void 67 | password_cache_save (const char *keygrip, const char *password) 68 | { 69 | #ifdef HAVE_LIBSECRET 70 | char *label; 71 | GError *error = NULL; 72 | 73 | if (! *keygrip) 74 | return; 75 | 76 | label = keygrip_to_label (keygrip); 77 | if (! label) 78 | return; 79 | 80 | if (! secret_password_store_sync (gpg_schema (), 81 | SECRET_COLLECTION_DEFAULT, 82 | label, password, NULL, &error, 83 | "stored-by", "GnuPG Pinentry", 84 | "keygrip", keygrip, NULL)) 85 | { 86 | printf("Failed to cache password for key %s with secret service: %s\n", 87 | keygrip, error->message); 88 | 89 | g_error_free (error); 90 | } 91 | 92 | free (label); 93 | #else 94 | return; 95 | #endif 96 | } 97 | 98 | char * 99 | password_cache_lookup (const char *keygrip) 100 | { 101 | #ifdef HAVE_LIBSECRET 102 | GError *error = NULL; 103 | char *password; 104 | char *password2; 105 | 106 | if (! *keygrip) 107 | return NULL; 108 | 109 | password = secret_password_lookup_nonpageable_sync 110 | (gpg_schema (), NULL, &error, 111 | "keygrip", keygrip, NULL); 112 | 113 | if (error != NULL) 114 | { 115 | printf("Failed to lookup password for key %s with secret service: %s\n", 116 | keygrip, error->message); 117 | g_error_free (error); 118 | return NULL; 119 | } 120 | if (! password) 121 | /* The password for this key is not cached. Just return NULL. */ 122 | return NULL; 123 | 124 | /* The password needs to be returned in secmem allocated memory. */ 125 | password2 = secmem_malloc (strlen (password) + 1); 126 | if (password2) 127 | strcpy(password2, password); 128 | else 129 | printf("secmem_malloc failed: can't copy password!\n"); 130 | 131 | secret_password_free (password); 132 | 133 | return password2; 134 | #else 135 | return NULL; 136 | #endif 137 | } 138 | 139 | /* Try and remove the cached password for key grip. Returns -1 on 140 | error, 0 if the key is not found and 1 if the password was 141 | removed. */ 142 | int 143 | password_cache_clear (const char *keygrip) 144 | { 145 | #ifdef HAVE_LIBSECRET 146 | GError *error = NULL; 147 | int removed = secret_password_clear_sync (gpg_schema (), NULL, &error, 148 | "keygrip", keygrip, NULL); 149 | if (error != NULL) 150 | { 151 | printf("Failed to clear password for key %s with secret service: %s\n", 152 | keygrip, error->message); 153 | g_debug("%s", error->message); 154 | g_error_free (error); 155 | return -1; 156 | } 157 | if (removed) 158 | return 1; 159 | return 0; 160 | #else 161 | return -1; 162 | #endif 163 | } 164 | -------------------------------------------------------------------------------- /pinentry/password-cache.h: -------------------------------------------------------------------------------- 1 | /* password-cache.h - Password cache support interfaces. 2 | Copyright (C) 2015 g10 Code GmbH 3 | 4 | This file is part of PINENTRY. 5 | 6 | PINENTRY is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | PINENTRY is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, see . 18 | */ 19 | 20 | #ifndef PASSWORD_CACHE_H 21 | #define PASSWORD_CACHE_H 22 | 23 | void password_cache_save (const char *key_grip, const char *password); 24 | 25 | char *password_cache_lookup (const char *key_grip); 26 | 27 | int password_cache_clear (const char *keygrip); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /pinentry/pinentry.c: -------------------------------------------------------------------------------- 1 | /* pinentry.c - The PIN entry support library 2 | Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015 g10 Code GmbH 3 | 4 | This file is part of PINENTRY. 5 | 6 | PINENTRY is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | PINENTRY is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "memory.h" 28 | #include "secmem-util.h" 29 | #include "argparse.h" 30 | #include "pinentry.h" 31 | #include "password-cache.h" 32 | 33 | /* Keep the name of our program here. */ 34 | static char this_pgmname[50]; 35 | 36 | struct pinentry pinentry; 37 | 38 | static void 39 | pinentry_reset (int use_defaults) 40 | { 41 | /* GPG Agent sets these options once when it starts the pinentry. 42 | Don't reset them. */ 43 | int grab = pinentry.grab; 44 | char *ttyname = pinentry.ttyname; 45 | char *ttytype = pinentry.ttytype; 46 | char *lc_ctype = pinentry.lc_ctype; 47 | char *lc_messages = pinentry.lc_messages; 48 | int allow_external_password_cache = pinentry.allow_external_password_cache; 49 | char *default_ok = pinentry.default_ok; 50 | char *default_cancel = pinentry.default_cancel; 51 | char *default_prompt = pinentry.default_prompt; 52 | char *default_pwmngr = pinentry.default_pwmngr; 53 | char *touch_file = pinentry.touch_file; 54 | 55 | /* These options are set from the command line. Don't reset 56 | them. */ 57 | int debug = pinentry.debug; 58 | char *display = pinentry.display; 59 | int parent_wid = pinentry.parent_wid; 60 | 61 | pinentry_color_t color_fg = pinentry.color_fg; 62 | int color_fg_bright = pinentry.color_fg_bright; 63 | pinentry_color_t color_bg = pinentry.color_bg; 64 | pinentry_color_t color_so = pinentry.color_so; 65 | int color_so_bright = pinentry.color_so_bright; 66 | 67 | int timout = pinentry.timeout; 68 | 69 | /* Free any allocated memory. */ 70 | if (use_defaults) 71 | { 72 | free (pinentry.ttyname); 73 | free (pinentry.ttytype); 74 | free (pinentry.lc_ctype); 75 | free (pinentry.lc_messages); 76 | free (pinentry.default_ok); 77 | free (pinentry.default_cancel); 78 | free (pinentry.default_prompt); 79 | free (pinentry.default_pwmngr); 80 | free (pinentry.touch_file); 81 | free (pinentry.display); 82 | } 83 | 84 | free (pinentry.title); 85 | free (pinentry.description); 86 | free (pinentry.error); 87 | free (pinentry.prompt); 88 | free (pinentry.ok); 89 | free (pinentry.notok); 90 | free (pinentry.cancel); 91 | secmem_free (pinentry.pin); 92 | free (pinentry.repeat_passphrase); 93 | free (pinentry.repeat_error_string); 94 | free (pinentry.quality_bar); 95 | free (pinentry.quality_bar_tt); 96 | free (pinentry.keyinfo); 97 | 98 | /* Reset the pinentry structure. */ 99 | memset (&pinentry, 0, sizeof (pinentry)); 100 | 101 | if (use_defaults) 102 | { 103 | /* Pinentry timeout in seconds. */ 104 | pinentry.timeout = 60; 105 | 106 | /* Global grab. */ 107 | pinentry.grab = 1; 108 | 109 | pinentry.color_fg = PINENTRY_COLOR_DEFAULT; 110 | pinentry.color_fg_bright = 0; 111 | pinentry.color_bg = PINENTRY_COLOR_DEFAULT; 112 | pinentry.color_so = PINENTRY_COLOR_DEFAULT; 113 | pinentry.color_so_bright = 0; 114 | } 115 | else 116 | /* Restore the options. */ 117 | { 118 | pinentry.grab = grab; 119 | pinentry.ttyname = ttyname; 120 | pinentry.ttytype = ttytype; 121 | pinentry.lc_ctype = lc_ctype; 122 | pinentry.lc_messages = lc_messages; 123 | pinentry.allow_external_password_cache = allow_external_password_cache; 124 | pinentry.default_ok = default_ok; 125 | pinentry.default_cancel = default_cancel; 126 | pinentry.default_prompt = default_prompt; 127 | pinentry.default_pwmngr = default_pwmngr; 128 | pinentry.touch_file = touch_file; 129 | 130 | pinentry.debug = debug; 131 | pinentry.display = display; 132 | pinentry.parent_wid = parent_wid; 133 | 134 | pinentry.color_fg = color_fg; 135 | pinentry.color_fg_bright = color_fg_bright; 136 | pinentry.color_bg = color_bg; 137 | pinentry.color_so = color_so; 138 | pinentry.color_so_bright = color_so_bright; 139 | 140 | pinentry.timeout = timout; 141 | } 142 | } 143 | 144 | static gpg_error_t 145 | pinentry_assuan_reset_handler (assuan_context_t ctx, char *line) 146 | { 147 | (void)ctx; 148 | (void)line; 149 | 150 | pinentry_reset (0); 151 | 152 | return 0; 153 | } 154 | 155 | 156 | 157 | static int lc_ctype_unknown_warning = 0; 158 | 159 | /* Copy TEXT or TEXTLEN to BUFFER and escape as required. Return a 160 | pointer to the end of the new buffer. Note that BUFFER must be 161 | large enough to keep the entire text; allocataing it 3 times of 162 | TEXTLEN is sufficient. */ 163 | static char * 164 | copy_and_escape (char *buffer, const void *text, size_t textlen) 165 | { 166 | int i; 167 | const unsigned char *s = (unsigned char *)text; 168 | char *p = buffer; 169 | 170 | for (i=0; i < textlen; i++) 171 | { 172 | if (s[i] < ' ' || s[i] == '+') 173 | { 174 | snprintf (p, 4, "%%%02X", s[i]); 175 | p += 3; 176 | } 177 | else if (s[i] == ' ') 178 | *p++ = '+'; 179 | else 180 | *p++ = s[i]; 181 | } 182 | return p; 183 | } 184 | 185 | 186 | 187 | /* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH 188 | because not all backends might be able to return a proper 189 | C-string.). Returns: A value between -100 and 100 to give an 190 | estimate of the passphrase's quality. Negative values are use if 191 | the caller won't even accept that passphrase. Note that we expect 192 | just one data line which should not be escaped in any represent a 193 | numeric signed decimal value. Extra data is currently ignored but 194 | should not be send at all. */ 195 | int 196 | pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length) 197 | { 198 | assuan_context_t ctx = pin->ctx_assuan; 199 | const char prefix[] = "INQUIRE QUALITY "; 200 | char *command; 201 | char *line; 202 | size_t linelen; 203 | int gotvalue = 0; 204 | int value = 0; 205 | int rc; 206 | 207 | if (!ctx) 208 | return 0; /* Can't run the callback. */ 209 | 210 | if (length > 300) 211 | length = 300; /* Limit so that it definitely fits into an Assuan 212 | line. */ 213 | 214 | command = secmem_malloc (strlen (prefix) + 3*length + 1); 215 | if (!command) 216 | return 0; 217 | strcpy (command, prefix); 218 | copy_and_escape (command + strlen(command), passphrase, length); 219 | rc = assuan_write_line (ctx, command); 220 | secmem_free (command); 221 | if (rc) 222 | { 223 | fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc); 224 | return 0; 225 | } 226 | 227 | for (;;) 228 | { 229 | do 230 | { 231 | rc = assuan_read_line (ctx, &line, &linelen); 232 | if (rc) 233 | { 234 | fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc); 235 | return 0; 236 | } 237 | } 238 | while (*line == '#' || !linelen); 239 | if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' 240 | && (!line[3] || line[3] == ' ')) 241 | break; /* END command received*/ 242 | if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N' 243 | && (!line[3] || line[3] == ' ')) 244 | break; /* CAN command received*/ 245 | if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' 246 | && (!line[3] || line[3] == ' ')) 247 | break; /* ERR command received*/ 248 | if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue) 249 | continue; 250 | gotvalue = 1; 251 | value = atoi (line+2); 252 | } 253 | if (value < -100) 254 | value = -100; 255 | else if (value > 100) 256 | value = 100; 257 | 258 | return value; 259 | } 260 | 261 | 262 | 263 | /* Try to make room for at least LEN bytes in the pinentry. Returns 264 | new buffer on success and 0 on failure or when the old buffer is 265 | sufficient. */ 266 | char * 267 | pinentry_setbufferlen (pinentry_t pin, int len) 268 | { 269 | char *newp; 270 | 271 | if (pin->pin_len) 272 | assert (pin->pin); 273 | else 274 | assert (!pin->pin); 275 | 276 | if (len < 2048) 277 | len = 2048; 278 | 279 | if (len <= pin->pin_len) 280 | return pin->pin; 281 | 282 | newp = secmem_realloc (pin->pin, len); 283 | if (newp) 284 | { 285 | pin->pin = newp; 286 | pin->pin_len = len; 287 | } 288 | else 289 | { 290 | secmem_free (pin->pin); 291 | pin->pin = 0; 292 | pin->pin_len = 0; 293 | } 294 | return newp; 295 | } 296 | 297 | static void 298 | pinentry_setbuffer_clear (pinentry_t pin) 299 | { 300 | if (! pin->pin) 301 | { 302 | assert (pin->pin_len == 0); 303 | return; 304 | } 305 | 306 | assert (pin->pin_len > 0); 307 | 308 | secmem_free (pin->pin); 309 | pin->pin = NULL; 310 | pin->pin_len = 0; 311 | } 312 | 313 | static void 314 | pinentry_setbuffer_init (pinentry_t pin) 315 | { 316 | pinentry_setbuffer_clear (pin); 317 | pinentry_setbufferlen (pin, 0); 318 | } 319 | 320 | /* passphrase better be alloced with secmem_alloc. */ 321 | void 322 | pinentry_setbuffer_use (pinentry_t pin, char *passphrase, int len) 323 | { 324 | if (! passphrase) 325 | { 326 | assert (len == 0); 327 | pinentry_setbuffer_clear (pin); 328 | 329 | return; 330 | } 331 | 332 | if (passphrase && len == 0) 333 | len = strlen (passphrase) + 1; 334 | 335 | if (pin->pin) 336 | secmem_free (pin->pin); 337 | 338 | pin->pin = passphrase; 339 | pin->pin_len = len; 340 | } 341 | 342 | static struct assuan_malloc_hooks assuan_malloc_hooks = { 343 | secmem_malloc, secmem_realloc, secmem_free 344 | }; 345 | 346 | /* Initialize the secure memory subsystem, drop privileges and return. 347 | Must be called early. */ 348 | void 349 | pinentry_init (const char *pgmname) 350 | { 351 | /* Store away our name. */ 352 | if (strlen (pgmname) > sizeof this_pgmname - 2) 353 | abort (); 354 | strcpy (this_pgmname, pgmname); 355 | 356 | gpgrt_check_version (NULL); 357 | 358 | /* Initialize secure memory. 1 is too small, so the default size 359 | will be used. */ 360 | secmem_init (1); 361 | secmem_set_flags (SECMEM_WARN); 362 | drop_privs (); 363 | 364 | if (atexit (secmem_term)) 365 | { 366 | /* FIXME: Could not register at-exit function, bail out. */ 367 | } 368 | 369 | assuan_set_malloc_hooks (&assuan_malloc_hooks); 370 | } 371 | 372 | /* Simple test to check whether DISPLAY is set or the option --display 373 | was given. Used to decide whether the GUI or curses should be 374 | initialized. */ 375 | int 376 | pinentry_have_display (int argc, char **argv) 377 | { 378 | for (; argc; argc--, argv++) 379 | if (!strcmp (*argv, "--display") || !strncmp (*argv, "--display=", 10)) 380 | return 1; 381 | return 0; 382 | } 383 | 384 | 385 | 386 | /* Print usage information and and provide strings for help. */ 387 | static const char * 388 | my_strusage( int level ) 389 | { 390 | const char *p; 391 | 392 | switch (level) 393 | { 394 | case 11: p = this_pgmname; break; 395 | case 12: p = "pinentry"; break; 396 | case 13: p = PACKAGE_VERSION; break; 397 | case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break; 398 | case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break; 399 | case 1: 400 | case 40: 401 | { 402 | static char *str; 403 | 404 | if (!str) 405 | { 406 | size_t n = 50 + strlen (this_pgmname); 407 | str = malloc (n); 408 | if (str) 409 | snprintf (str, n, "Usage: %s [options] (-h for help)", 410 | this_pgmname); 411 | } 412 | p = str; 413 | } 414 | break; 415 | case 41: 416 | p = "Ask securely for a secret and print it to stdout."; 417 | break; 418 | 419 | case 42: 420 | p = "1"; /* Flag print 40 as part of 41. */ 421 | break; 422 | 423 | default: p = NULL; break; 424 | } 425 | return p; 426 | } 427 | 428 | 429 | char * 430 | parse_color (char *arg, pinentry_color_t *color_p, int *bright_p) 431 | { 432 | static struct 433 | { 434 | const char *name; 435 | pinentry_color_t color; 436 | } colors[] = { { "none", PINENTRY_COLOR_NONE }, 437 | { "default", PINENTRY_COLOR_DEFAULT }, 438 | { "black", PINENTRY_COLOR_BLACK }, 439 | { "red", PINENTRY_COLOR_RED }, 440 | { "green", PINENTRY_COLOR_GREEN }, 441 | { "yellow", PINENTRY_COLOR_YELLOW }, 442 | { "blue", PINENTRY_COLOR_BLUE }, 443 | { "magenta", PINENTRY_COLOR_MAGENTA }, 444 | { "cyan", PINENTRY_COLOR_CYAN }, 445 | { "white", PINENTRY_COLOR_WHITE } }; 446 | 447 | int i; 448 | char *new_arg; 449 | pinentry_color_t color = PINENTRY_COLOR_DEFAULT; 450 | 451 | if (!arg) 452 | return NULL; 453 | 454 | new_arg = strchr (arg, ','); 455 | if (new_arg) 456 | new_arg++; 457 | 458 | if (bright_p) 459 | { 460 | const char *bname[] = { "bright-", "bright", "bold-", "bold" }; 461 | 462 | *bright_p = 0; 463 | for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++) 464 | if (!strncasecmp (arg, bname[i], strlen (bname[i]))) 465 | { 466 | *bright_p = 1; 467 | arg += strlen (bname[i]); 468 | } 469 | } 470 | 471 | for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++) 472 | if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name))) 473 | color = colors[i].color; 474 | 475 | *color_p = color; 476 | return new_arg; 477 | } 478 | 479 | /* Parse the command line options. May exit the program if only help 480 | or version output is requested. */ 481 | void 482 | pinentry_parse_opts (int argc, char *argv[]) 483 | { 484 | static ARGPARSE_OPTS opts[] = { 485 | ARGPARSE_s_n('d', "debug", "Turn on debugging output"), 486 | ARGPARSE_s_s('D', "display", "|DISPLAY|Set the X display"), 487 | ARGPARSE_s_s('T', "ttyname", "|FILE|Set the tty terminal node name"), 488 | ARGPARSE_s_s('N', "ttytype", "|NAME|Set the tty terminal type"), 489 | ARGPARSE_s_s('C', "lc-ctype", "|STRING|Set the tty LC_CTYPE value"), 490 | ARGPARSE_s_s('M', "lc-messages", "|STRING|Set the tty LC_MESSAGES value"), 491 | ARGPARSE_s_i('o', "timeout", 492 | "|SECS|Timeout waiting for input after this many seconds"), 493 | ARGPARSE_s_n('g', "no-global-grab", 494 | "Grab keyboard only while window is focused"), 495 | ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"), 496 | ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"), 497 | ARGPARSE_end() 498 | }; 499 | ARGPARSE_ARGS pargs = { &argc, &argv, 0 }; 500 | 501 | set_strusage (my_strusage); 502 | 503 | pinentry_reset (1); 504 | 505 | while (arg_parse (&pargs, opts)) 506 | { 507 | switch (pargs.r_opt) 508 | { 509 | case 'd': 510 | pinentry.debug = 1; 511 | break; 512 | case 'g': 513 | pinentry.grab = 0; 514 | break; 515 | 516 | case 'D': 517 | /* Note, this is currently not used because the GUI engine 518 | has already been initialized when parsing these options. */ 519 | pinentry.display = strdup (pargs.r.ret_str); 520 | if (!pinentry.display) 521 | { 522 | exit (EXIT_FAILURE); 523 | } 524 | break; 525 | case 'T': 526 | pinentry.ttyname = strdup (pargs.r.ret_str); 527 | if (!pinentry.ttyname) 528 | { 529 | exit (EXIT_FAILURE); 530 | } 531 | break; 532 | case 'N': 533 | pinentry.ttytype = strdup (pargs.r.ret_str); 534 | if (!pinentry.ttytype) 535 | { 536 | exit (EXIT_FAILURE); 537 | } 538 | break; 539 | case 'C': 540 | pinentry.lc_ctype = strdup (pargs.r.ret_str); 541 | if (!pinentry.lc_ctype) 542 | { 543 | exit (EXIT_FAILURE); 544 | } 545 | break; 546 | case 'M': 547 | pinentry.lc_messages = strdup (pargs.r.ret_str); 548 | if (!pinentry.lc_messages) 549 | { 550 | exit (EXIT_FAILURE); 551 | } 552 | break; 553 | case 'W': 554 | pinentry.parent_wid = pargs.r.ret_ulong; 555 | break; 556 | 557 | case 'c': 558 | { 559 | char *tmpstr = pargs.r.ret_str; 560 | 561 | tmpstr = parse_color (tmpstr, &pinentry.color_fg, 562 | &pinentry.color_fg_bright); 563 | tmpstr = parse_color (tmpstr, &pinentry.color_bg, NULL); 564 | tmpstr = parse_color (tmpstr, &pinentry.color_so, 565 | &pinentry.color_so_bright); 566 | } 567 | break; 568 | 569 | case 'o': 570 | pinentry.timeout = pargs.r.ret_int; 571 | break; 572 | 573 | default: 574 | pargs.err = ARGPARSE_PRINT_WARNING; 575 | break; 576 | } 577 | } 578 | } 579 | 580 | 581 | static gpg_error_t 582 | option_handler (assuan_context_t ctx, const char *key, const char *value) 583 | { 584 | (void)ctx; 585 | 586 | if (!strcmp (key, "no-grab") && !*value) 587 | pinentry.grab = 0; 588 | else if (!strcmp (key, "grab") && !*value) 589 | pinentry.grab = 1; 590 | else if (!strcmp (key, "debug-wait")) 591 | { 592 | } 593 | else if (!strcmp (key, "display")) 594 | { 595 | if (pinentry.display) 596 | free (pinentry.display); 597 | pinentry.display = strdup (value); 598 | if (!pinentry.display) 599 | return gpg_error_from_syserror (); 600 | } 601 | else if (!strcmp (key, "ttyname")) 602 | { 603 | if (pinentry.ttyname) 604 | free (pinentry.ttyname); 605 | pinentry.ttyname = strdup (value); 606 | if (!pinentry.ttyname) 607 | return gpg_error_from_syserror (); 608 | } 609 | else if (!strcmp (key, "ttytype")) 610 | { 611 | if (pinentry.ttytype) 612 | free (pinentry.ttytype); 613 | pinentry.ttytype = strdup (value); 614 | if (!pinentry.ttytype) 615 | return gpg_error_from_syserror (); 616 | } 617 | else if (!strcmp (key, "lc-ctype")) 618 | { 619 | if (pinentry.lc_ctype) 620 | free (pinentry.lc_ctype); 621 | pinentry.lc_ctype = strdup (value); 622 | if (!pinentry.lc_ctype) 623 | return gpg_error_from_syserror (); 624 | } 625 | else if (!strcmp (key, "lc-messages")) 626 | { 627 | if (pinentry.lc_messages) 628 | free (pinentry.lc_messages); 629 | pinentry.lc_messages = strdup (value); 630 | if (!pinentry.lc_messages) 631 | return gpg_error_from_syserror (); 632 | } 633 | else if (!strcmp (key, "parent-wid")) 634 | { 635 | pinentry.parent_wid = atoi (value); 636 | /* FIXME: Use strtol and add some error handling. */ 637 | } 638 | else if (!strcmp (key, "touch-file")) 639 | { 640 | if (pinentry.touch_file) 641 | free (pinentry.touch_file); 642 | pinentry.touch_file = strdup (value); 643 | if (!pinentry.touch_file) 644 | return gpg_error_from_syserror (); 645 | } 646 | else if (!strcmp (key, "default-ok")) 647 | { 648 | pinentry.default_ok = strdup (value); 649 | if (!pinentry.default_ok) 650 | return gpg_error_from_syserror (); 651 | } 652 | else if (!strcmp (key, "default-cancel")) 653 | { 654 | pinentry.default_cancel = strdup (value); 655 | if (!pinentry.default_cancel) 656 | return gpg_error_from_syserror (); 657 | } 658 | else if (!strcmp (key, "default-prompt")) 659 | { 660 | pinentry.default_prompt = strdup (value); 661 | if (!pinentry.default_prompt) 662 | return gpg_error_from_syserror (); 663 | } 664 | else if (!strcmp (key, "default-pwmngr")) 665 | { 666 | pinentry.default_pwmngr = strdup (value); 667 | if (!pinentry.default_pwmngr) 668 | return gpg_error_from_syserror (); 669 | } 670 | else if (!strcmp (key, "allow-external-password-cache") && !*value) 671 | { 672 | pinentry.allow_external_password_cache = 1; 673 | pinentry.tried_password_cache = 0; 674 | } 675 | else if (!strcmp (key, "allow-emacs-prompt") && !*value) 676 | { 677 | return gpg_error (GPG_ERR_NOT_SUPPORTED); 678 | } 679 | else 680 | return gpg_error (GPG_ERR_UNKNOWN_OPTION); 681 | return 0; 682 | } 683 | 684 | 685 | /* Note, that it is sufficient to allocate the target string D as 686 | long as the source string S, i.e.: strlen(s)+1; */ 687 | static void 688 | strcpy_escaped (char *d, const char *s) 689 | { 690 | while (*s) 691 | { 692 | if (*s == '%' && s[1] && s[2]) 693 | { 694 | s++; 695 | *d++ = xtoi_2 ( s); 696 | s += 2; 697 | } 698 | else 699 | *d++ = *s++; 700 | } 701 | *d = 0; 702 | } 703 | 704 | 705 | static gpg_error_t 706 | cmd_setdesc (assuan_context_t ctx, char *line) 707 | { 708 | char *newd; 709 | 710 | (void)ctx; 711 | 712 | newd = malloc (strlen (line) + 1); 713 | if (!newd) 714 | return gpg_error_from_syserror (); 715 | 716 | strcpy_escaped (newd, line); 717 | if (pinentry.description) 718 | free (pinentry.description); 719 | pinentry.description = newd; 720 | return 0; 721 | } 722 | 723 | 724 | static gpg_error_t 725 | cmd_setprompt (assuan_context_t ctx, char *line) 726 | { 727 | char *newp; 728 | 729 | (void)ctx; 730 | 731 | newp = malloc (strlen (line) + 1); 732 | if (!newp) 733 | return gpg_error_from_syserror (); 734 | 735 | strcpy_escaped (newp, line); 736 | if (pinentry.prompt) 737 | free (pinentry.prompt); 738 | pinentry.prompt = newp; 739 | return 0; 740 | } 741 | 742 | 743 | /* The data provided at LINE may be used by pinentry implementations 744 | to identify a key for caching strategies of its own. The empty 745 | string and --clear mean that the key does not have a stable 746 | identifier. */ 747 | static gpg_error_t 748 | cmd_setkeyinfo (assuan_context_t ctx, char *line) 749 | { 750 | (void)ctx; 751 | 752 | if (pinentry.keyinfo) 753 | free (pinentry.keyinfo); 754 | 755 | if (*line && strcmp(line, "--clear") != 0) 756 | pinentry.keyinfo = strdup (line); 757 | else 758 | pinentry.keyinfo = NULL; 759 | 760 | return 0; 761 | } 762 | 763 | 764 | static gpg_error_t 765 | cmd_setrepeat (assuan_context_t ctx, char *line) 766 | { 767 | char *p; 768 | 769 | (void)ctx; 770 | 771 | p = malloc (strlen (line) + 1); 772 | if (!p) 773 | return gpg_error_from_syserror (); 774 | 775 | strcpy_escaped (p, line); 776 | free (pinentry.repeat_passphrase); 777 | pinentry.repeat_passphrase = p; 778 | return 0; 779 | } 780 | 781 | 782 | static gpg_error_t 783 | cmd_setrepeaterror (assuan_context_t ctx, char *line) 784 | { 785 | char *p; 786 | 787 | (void)ctx; 788 | 789 | p = malloc (strlen (line) + 1); 790 | if (!p) 791 | return gpg_error_from_syserror (); 792 | 793 | strcpy_escaped (p, line); 794 | free (pinentry.repeat_error_string); 795 | pinentry.repeat_error_string = p; 796 | return 0; 797 | } 798 | 799 | 800 | static gpg_error_t 801 | cmd_seterror (assuan_context_t ctx, char *line) 802 | { 803 | char *newe; 804 | 805 | (void)ctx; 806 | 807 | newe = malloc (strlen (line) + 1); 808 | if (!newe) 809 | return gpg_error_from_syserror (); 810 | 811 | strcpy_escaped (newe, line); 812 | if (pinentry.error) 813 | free (pinentry.error); 814 | pinentry.error = newe; 815 | return 0; 816 | } 817 | 818 | 819 | static gpg_error_t 820 | cmd_setok (assuan_context_t ctx, char *line) 821 | { 822 | char *newo; 823 | 824 | (void)ctx; 825 | 826 | newo = malloc (strlen (line) + 1); 827 | if (!newo) 828 | return gpg_error_from_syserror (); 829 | 830 | strcpy_escaped (newo, line); 831 | if (pinentry.ok) 832 | free (pinentry.ok); 833 | pinentry.ok = newo; 834 | return 0; 835 | } 836 | 837 | 838 | static gpg_error_t 839 | cmd_setnotok (assuan_context_t ctx, char *line) 840 | { 841 | char *newo; 842 | 843 | (void)ctx; 844 | 845 | newo = malloc (strlen (line) + 1); 846 | if (!newo) 847 | return gpg_error_from_syserror (); 848 | 849 | strcpy_escaped (newo, line); 850 | if (pinentry.notok) 851 | free (pinentry.notok); 852 | pinentry.notok = newo; 853 | return 0; 854 | } 855 | 856 | 857 | static gpg_error_t 858 | cmd_setcancel (assuan_context_t ctx, char *line) 859 | { 860 | char *newc; 861 | 862 | (void)ctx; 863 | 864 | newc = malloc (strlen (line) + 1); 865 | if (!newc) 866 | return gpg_error_from_syserror (); 867 | 868 | strcpy_escaped (newc, line); 869 | if (pinentry.cancel) 870 | free (pinentry.cancel); 871 | pinentry.cancel = newc; 872 | return 0; 873 | } 874 | 875 | 876 | static gpg_error_t 877 | cmd_settimeout (assuan_context_t ctx, char *line) 878 | { 879 | (void)ctx; 880 | 881 | if (line && *line) 882 | pinentry.timeout = atoi (line); 883 | 884 | return 0; 885 | } 886 | 887 | static gpg_error_t 888 | cmd_settitle (assuan_context_t ctx, char *line) 889 | { 890 | char *newt; 891 | 892 | (void)ctx; 893 | 894 | newt = malloc (strlen (line) + 1); 895 | if (!newt) 896 | return gpg_error_from_syserror (); 897 | 898 | strcpy_escaped (newt, line); 899 | if (pinentry.title) 900 | free (pinentry.title); 901 | pinentry.title = newt; 902 | return 0; 903 | } 904 | 905 | static gpg_error_t 906 | cmd_setqualitybar (assuan_context_t ctx, char *line) 907 | { 908 | char *newval; 909 | 910 | (void)ctx; 911 | 912 | if (!*line) 913 | line = "Quality:"; 914 | 915 | newval = malloc (strlen (line) + 1); 916 | if (!newval) 917 | return gpg_error_from_syserror (); 918 | 919 | strcpy_escaped (newval, line); 920 | if (pinentry.quality_bar) 921 | free (pinentry.quality_bar); 922 | pinentry.quality_bar = newval; 923 | return 0; 924 | } 925 | 926 | /* Set the tooltip to be used for a quality bar. */ 927 | static gpg_error_t 928 | cmd_setqualitybar_tt (assuan_context_t ctx, char *line) 929 | { 930 | char *newval; 931 | 932 | (void)ctx; 933 | 934 | if (*line) 935 | { 936 | newval = malloc (strlen (line) + 1); 937 | if (!newval) 938 | return gpg_error_from_syserror (); 939 | 940 | strcpy_escaped (newval, line); 941 | } 942 | else 943 | newval = NULL; 944 | if (pinentry.quality_bar_tt) 945 | free (pinentry.quality_bar_tt); 946 | pinentry.quality_bar_tt = newval; 947 | return 0; 948 | } 949 | 950 | 951 | static gpg_error_t 952 | cmd_getpin (assuan_context_t ctx, char *line) 953 | { 954 | int result; 955 | int set_prompt = 0; 956 | int just_read_password_from_cache = 0; 957 | 958 | (void)line; 959 | 960 | pinentry_setbuffer_init (&pinentry); 961 | if (!pinentry.pin) 962 | return gpg_error (GPG_ERR_ENOMEM); 963 | 964 | /* Try reading from the password cache. */ 965 | if (/* If repeat passphrase is set, then we don't want to read from 966 | the cache. */ 967 | ! pinentry.repeat_passphrase 968 | /* Are we allowed to read from the cache? */ 969 | && pinentry.allow_external_password_cache 970 | && pinentry.keyinfo 971 | /* Only read from the cache if we haven't already tried it. */ 972 | && ! pinentry.tried_password_cache 973 | /* If the last read resulted in an error, then don't read from 974 | the cache. */ 975 | && ! pinentry.error) 976 | { 977 | char *password; 978 | 979 | pinentry.tried_password_cache = 1; 980 | 981 | password = password_cache_lookup (pinentry.keyinfo); 982 | if (password) 983 | /* There is a cached password. Try it. */ 984 | { 985 | int len = strlen(password) + 1; 986 | if (len > pinentry.pin_len) 987 | len = pinentry.pin_len; 988 | 989 | memcpy (pinentry.pin, password, len); 990 | pinentry.pin[len] = '\0'; 991 | 992 | secmem_free (password); 993 | 994 | pinentry.pin_from_cache = 1; 995 | 996 | assuan_write_status (ctx, "PASSWORD_FROM_CACHE", ""); 997 | 998 | /* Result is the length of the password not including the 999 | NUL terminator. */ 1000 | result = len - 1; 1001 | 1002 | just_read_password_from_cache = 1; 1003 | 1004 | goto out; 1005 | } 1006 | } 1007 | 1008 | /* The password was not cached (or we are not allowed to / cannot 1009 | use the cache). Prompt the user. */ 1010 | pinentry.pin_from_cache = 0; 1011 | 1012 | if (!pinentry.prompt) 1013 | { 1014 | pinentry.prompt = pinentry.default_prompt?pinentry.default_prompt:"PIN:"; 1015 | set_prompt = 1; 1016 | } 1017 | pinentry.locale_err = 0; 1018 | pinentry.specific_err = 0; 1019 | pinentry.close_button = 0; 1020 | pinentry.repeat_okay = 0; 1021 | pinentry.one_button = 0; 1022 | pinentry.ctx_assuan = ctx; 1023 | result = (*pinentry_cmd_handler) (&pinentry); 1024 | pinentry.ctx_assuan = NULL; 1025 | if (pinentry.error) 1026 | { 1027 | free (pinentry.error); 1028 | pinentry.error = NULL; 1029 | } 1030 | if (pinentry.repeat_passphrase) 1031 | { 1032 | free (pinentry.repeat_passphrase); 1033 | pinentry.repeat_passphrase = NULL; 1034 | } 1035 | if (set_prompt) 1036 | pinentry.prompt = NULL; 1037 | 1038 | pinentry.quality_bar = 0; /* Reset it after the command. */ 1039 | 1040 | if (pinentry.close_button) 1041 | assuan_write_status (ctx, "BUTTON_INFO", "close"); 1042 | 1043 | if (result < 0) 1044 | { 1045 | pinentry_setbuffer_clear (&pinentry); 1046 | if (pinentry.specific_err) 1047 | return pinentry.specific_err; 1048 | return (pinentry.locale_err 1049 | ? gpg_error (GPG_ERR_LOCALE_PROBLEM) 1050 | : gpg_error (GPG_ERR_CANCELED)); 1051 | } 1052 | 1053 | out: 1054 | if (result) 1055 | { 1056 | if (pinentry.repeat_okay) 1057 | assuan_write_status (ctx, "PIN_REPEATED", ""); 1058 | result = assuan_send_data (ctx, pinentry.pin, strlen(pinentry.pin)); 1059 | if (!result) 1060 | result = assuan_send_data (ctx, NULL, 0); 1061 | 1062 | if (/* GPG Agent says it's okay. */ 1063 | pinentry.allow_external_password_cache && pinentry.keyinfo 1064 | /* We didn't just read it from the cache. */ 1065 | && ! just_read_password_from_cache 1066 | /* And the user said it's okay. */ 1067 | && pinentry.may_cache_password) 1068 | /* Cache the password. */ 1069 | password_cache_save (pinentry.keyinfo, pinentry.pin); 1070 | } 1071 | 1072 | pinentry_setbuffer_clear (&pinentry); 1073 | 1074 | return result; 1075 | } 1076 | 1077 | 1078 | /* Note that the option --one-button is a hack to allow the use of old 1079 | pinentries while the caller is ignoring the result. Given that 1080 | options have never been used or flagged as an error the new option 1081 | is an easy way to enable the messsage mode while not requiring to 1082 | update pinentry or to have the caller test for the message 1083 | command. New applications which are free to require an updated 1084 | pinentry should use MESSAGE instead. */ 1085 | static gpg_error_t 1086 | cmd_confirm (assuan_context_t ctx, char *line) 1087 | { 1088 | int result; 1089 | 1090 | pinentry.one_button = !!strstr (line, "--one-button"); 1091 | pinentry.quality_bar = 0; 1092 | pinentry.close_button = 0; 1093 | pinentry.locale_err = 0; 1094 | pinentry.specific_err = 0; 1095 | pinentry.canceled = 0; 1096 | pinentry_setbuffer_clear (&pinentry); 1097 | result = (*pinentry_cmd_handler) (&pinentry); 1098 | if (pinentry.error) 1099 | { 1100 | free (pinentry.error); 1101 | pinentry.error = NULL; 1102 | } 1103 | 1104 | if (pinentry.close_button) 1105 | assuan_write_status (ctx, "BUTTON_INFO", "close"); 1106 | 1107 | if (result) 1108 | return 0; 1109 | 1110 | if (pinentry.specific_err) 1111 | return pinentry.specific_err; 1112 | 1113 | if (pinentry.locale_err) 1114 | return gpg_error (GPG_ERR_LOCALE_PROBLEM); 1115 | 1116 | if (pinentry.one_button) 1117 | return 0; 1118 | 1119 | if (pinentry.canceled) 1120 | return gpg_error (GPG_ERR_CANCELED); 1121 | return gpg_error (GPG_ERR_NOT_CONFIRMED); 1122 | } 1123 | 1124 | 1125 | static gpg_error_t 1126 | cmd_message (assuan_context_t ctx, char *line) 1127 | { 1128 | (void)line; 1129 | 1130 | return cmd_confirm (ctx, "--one-button"); 1131 | } 1132 | 1133 | /* GETINFO 1134 | 1135 | Multipurpose function to return a variety of information. 1136 | Supported values for WHAT are: 1137 | 1138 | version - Return the version of the program. 1139 | pid - Return the process id of the server. 1140 | */ 1141 | static gpg_error_t 1142 | cmd_getinfo (assuan_context_t ctx, char *line) 1143 | { 1144 | int rc; 1145 | 1146 | if (!strcmp (line, "version")) 1147 | { 1148 | const char *s = VERSION; 1149 | rc = assuan_send_data (ctx, s, strlen (s)); 1150 | } 1151 | else if (!strcmp (line, "pid")) 1152 | { 1153 | char numbuf[50]; 1154 | 1155 | snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ()); 1156 | rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); 1157 | } 1158 | else 1159 | rc = gpg_error (GPG_ERR_ASS_PARAMETER); 1160 | return rc; 1161 | } 1162 | 1163 | /* CLEARPASSPHRASE 1164 | 1165 | Clear the cache passphrase associated with the key identified by 1166 | cacheid. 1167 | */ 1168 | static gpg_error_t 1169 | cmd_clear_passphrase (assuan_context_t ctx, char *line) 1170 | { 1171 | (void)ctx; 1172 | 1173 | if (! line) 1174 | return gpg_error (GPG_ERR_ASS_INV_VALUE); 1175 | 1176 | /* Remove leading and trailing white space. */ 1177 | while (*line == ' ') 1178 | line ++; 1179 | while (line[strlen (line) - 1] == ' ') 1180 | line[strlen (line) - 1] = 0; 1181 | 1182 | switch (password_cache_clear (line)) 1183 | { 1184 | case 1: return 0; 1185 | case 0: return gpg_error (GPG_ERR_ASS_INV_VALUE); 1186 | default: return gpg_error (GPG_ERR_ASS_GENERAL); 1187 | } 1188 | } 1189 | 1190 | /* Tell the assuan library about our commands. */ 1191 | static gpg_error_t 1192 | register_commands (assuan_context_t ctx) 1193 | { 1194 | static struct 1195 | { 1196 | const char *name; 1197 | gpg_error_t (*handler) (assuan_context_t, char *line); 1198 | } table[] = 1199 | { 1200 | { "SETDESC", cmd_setdesc }, 1201 | { "SETPROMPT", cmd_setprompt }, 1202 | { "SETKEYINFO", cmd_setkeyinfo }, 1203 | { "SETREPEAT", cmd_setrepeat }, 1204 | { "SETREPEATERROR", cmd_setrepeaterror }, 1205 | { "SETERROR", cmd_seterror }, 1206 | { "SETOK", cmd_setok }, 1207 | { "SETNOTOK", cmd_setnotok }, 1208 | { "SETCANCEL", cmd_setcancel }, 1209 | { "GETPIN", cmd_getpin }, 1210 | { "CONFIRM", cmd_confirm }, 1211 | { "MESSAGE", cmd_message }, 1212 | { "SETQUALITYBAR", cmd_setqualitybar }, 1213 | { "SETQUALITYBAR_TT", cmd_setqualitybar_tt }, 1214 | { "GETINFO", cmd_getinfo }, 1215 | { "SETTITLE", cmd_settitle }, 1216 | { "SETTIMEOUT", cmd_settimeout }, 1217 | { "CLEARPASSPHRASE", cmd_clear_passphrase }, 1218 | { NULL } 1219 | }; 1220 | int i, j; 1221 | gpg_error_t rc; 1222 | 1223 | for (i = j = 0; table[i].name; i++) 1224 | { 1225 | rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL); 1226 | if (rc) 1227 | return rc; 1228 | } 1229 | return 0; 1230 | } 1231 | 1232 | 1233 | int 1234 | pinentry_loop2 (int infd, int outfd) 1235 | { 1236 | gpg_error_t rc; 1237 | assuan_fd_t filedes[2]; 1238 | assuan_context_t ctx; 1239 | 1240 | /* Extra check to make sure we have dropped privs. */ 1241 | if (getuid() != geteuid()) 1242 | abort (); 1243 | 1244 | rc = assuan_new (&ctx); 1245 | if (rc) 1246 | { 1247 | fprintf (stderr, "server context creation failed: %s\n", 1248 | gpg_strerror (rc)); 1249 | return -1; 1250 | } 1251 | 1252 | /* For now we use a simple pipe based server so that we can work 1253 | from scripts. We will later add options to run as a daemon and 1254 | wait for requests on a Unix domain socket. */ 1255 | filedes[0] = assuan_fdopen (infd); 1256 | filedes[1] = assuan_fdopen (outfd); 1257 | rc = assuan_init_pipe_server (ctx, filedes); 1258 | if (rc) 1259 | { 1260 | fprintf (stderr, "%s: failed to initialize the server: %s\n", 1261 | this_pgmname, gpg_strerror (rc)); 1262 | return -1; 1263 | } 1264 | rc = register_commands (ctx); 1265 | if (rc) 1266 | { 1267 | fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n", 1268 | this_pgmname, gpg_strerror (rc)); 1269 | return -1; 1270 | } 1271 | 1272 | assuan_register_option_handler (ctx, option_handler); 1273 | assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler); 1274 | 1275 | for (;;) 1276 | { 1277 | rc = assuan_accept (ctx); 1278 | if (rc == -1) 1279 | break; 1280 | else if (rc) 1281 | { 1282 | fprintf (stderr, "%s: Assuan accept problem: %s\n", 1283 | this_pgmname, gpg_strerror (rc)); 1284 | break; 1285 | } 1286 | 1287 | rc = assuan_process (ctx); 1288 | if (rc) 1289 | { 1290 | fprintf (stderr, "%s: Assuan processing failed: %s\n", 1291 | this_pgmname, gpg_strerror (rc)); 1292 | continue; 1293 | } 1294 | } 1295 | 1296 | assuan_release (ctx); 1297 | return 0; 1298 | } 1299 | 1300 | 1301 | /* Start the pinentry event loop. The program will start to process 1302 | Assuan commands until it is finished or an error occurs. If an 1303 | error occurs, -1 is returned. Otherwise, 0 is returned. */ 1304 | int 1305 | pinentry_loop (void) 1306 | { 1307 | return pinentry_loop2 (STDIN_FILENO, STDOUT_FILENO); 1308 | } 1309 | -------------------------------------------------------------------------------- /pinentry/pinentry.h: -------------------------------------------------------------------------------- 1 | /* pinentry.h - The interface for the PIN entry support library. 2 | Copyright (C) 2002, 2003, 2010, 2015 g10 Code GmbH 3 | 4 | This file is part of PINENTRY. 5 | 6 | PINENTRY is free software; you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | PINENTRY is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, see . 18 | */ 19 | 20 | #ifndef PINENTRY_H 21 | #define PINENTRY_H 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #if 0 26 | } 27 | #endif 28 | #endif 29 | 30 | typedef enum { 31 | PINENTRY_COLOR_NONE, PINENTRY_COLOR_DEFAULT, 32 | PINENTRY_COLOR_BLACK, PINENTRY_COLOR_RED, 33 | PINENTRY_COLOR_GREEN, PINENTRY_COLOR_YELLOW, 34 | PINENTRY_COLOR_BLUE, PINENTRY_COLOR_MAGENTA, 35 | PINENTRY_COLOR_CYAN, PINENTRY_COLOR_WHITE 36 | } pinentry_color_t; 37 | 38 | struct pinentry 39 | { 40 | /* The window title, or NULL. (Assuan: "SETTITLE TITLE".) */ 41 | char *title; 42 | /* The description to display, or NULL. (Assuan: "SETDESC 43 | DESC".) */ 44 | char *description; 45 | /* The error message to display, or NULL. (Assuan: "SETERROR 46 | MESSAGE".) */ 47 | char *error; 48 | /* The prompt to display, or NULL. (Assuan: "SETPROMPT 49 | prompt".) */ 50 | char *prompt; 51 | /* The OK button text to display, or NULL. (Assuan: "SETOK 52 | OK".) */ 53 | char *ok; 54 | /* The Not-OK button text to display, or NULL. This is the text for 55 | the alternative option shown by the third button. (Assuan: 56 | "SETNOTOK NOTOK".) */ 57 | char *notok; 58 | /* The Cancel button text to display, or NULL. (Assuan: "SETCANCEL 59 | CANCEL".) */ 60 | char *cancel; 61 | 62 | /* The buffer to store the secret into. */ 63 | char *pin; 64 | /* The length of the buffer. */ 65 | int pin_len; 66 | /* Whether the pin was read from an external cache (1) or entered by 67 | the user (0). */ 68 | int pin_from_cache; 69 | 70 | /* The name of the X display to use if X is available and supported. 71 | (Assuan: "OPTION display DISPLAY".) */ 72 | char *display; 73 | /* The name of the terminal node to open if X not available or 74 | supported. (Assuan: "OPTION ttyname TTYNAME".) */ 75 | char *ttyname; 76 | /* The type of the terminal. (Assuan: "OPTION ttytype TTYTYPE".) */ 77 | char *ttytype; 78 | /* The LC_CTYPE value for the terminal. (Assuan: "OPTION lc-ctype 79 | LC_CTYPE".) */ 80 | char *lc_ctype; 81 | /* The LC_MESSAGES value for the terminal. (Assuan: "OPTION 82 | lc-messages LC_MESSAGES".) */ 83 | char *lc_messages; 84 | 85 | /* True if debug mode is requested. */ 86 | int debug; 87 | 88 | /* The number of seconds before giving up while waiting for user input. */ 89 | int timeout; 90 | 91 | /* True if caller should grab the keyboard. (Assuan: "OPTION grab" 92 | or "OPTION no-grab".) */ 93 | int grab; 94 | /* The window ID of the parent window over which the pinentry window 95 | should be displayed. (Assuan: "OPTION parent-wid WID".) */ 96 | int parent_wid; 97 | 98 | /* The name of an optional file which will be touched after a curses 99 | entry has been displayed. (Assuan: "OPTION touch-file 100 | FILENAME".) */ 101 | char *touch_file; 102 | 103 | /* The frontend should set this to -1 if the user canceled the 104 | request, and to the length of the PIN stored in pin 105 | otherwise. */ 106 | int result; 107 | 108 | /* The frontend should set this if the NOTOK button was pressed. */ 109 | int canceled; 110 | 111 | /* The frontend should set this to true if an error with the local 112 | conversion occured. */ 113 | int locale_err; 114 | 115 | /* The frontend should set this to a gpg-error so that commands are 116 | able to return specific error codes. This is an ugly hack due to 117 | the fact that pinentry_cmd_handler_t returns the length of the 118 | passphrase or a negative error code. */ 119 | int specific_err; 120 | 121 | /* The frontend should set this to true if the window close button 122 | has been used. This flag is used in addition to a regular return 123 | value. */ 124 | int close_button; 125 | 126 | /* The caller should set this to true if only one button is 127 | required. This is useful for notification dialogs where only a 128 | dismiss button is required. */ 129 | int one_button; 130 | 131 | /* If true a second prompt for the passphrase is shown and the user 132 | is expected to enter the same passphrase again. Pinentry checks 133 | that both match. (Assuan: "SETREPEAT".) */ 134 | char *repeat_passphrase; 135 | 136 | /* The string to show if a repeated passphrase does not match. 137 | (Assuan: "SETREPEATERROR ERROR".) */ 138 | char *repeat_error_string; 139 | 140 | /* Set to true if the passphrase has been entered a second time and 141 | matches the first passphrase. */ 142 | int repeat_okay; 143 | 144 | /* If this is not NULL, a passphrase quality indicator is shown. 145 | There will also be an inquiry back to the caller to get an 146 | indication of the quality for the passphrase entered so far. The 147 | string is used as a label for the quality bar. (Assuan: 148 | "SETQUALITYBAR LABEL".) */ 149 | char *quality_bar; 150 | 151 | /* The tooltip to be show for the qualitybar. Malloced or NULL. 152 | (Assuan: "SETQUALITYBAR_TT TOOLTIP".) */ 153 | char *quality_bar_tt; 154 | 155 | /* For the curses pinentry, the color of error messages. */ 156 | pinentry_color_t color_fg; 157 | int color_fg_bright; 158 | pinentry_color_t color_bg; 159 | pinentry_color_t color_so; 160 | int color_so_bright; 161 | 162 | /* Malloced and i18ned default strings or NULL. These strings may 163 | include an underscore character to indicate an accelerator key. 164 | A double underscore represents a plain one. */ 165 | /* (Assuan: "OPTION default-ok OK"). */ 166 | char *default_ok; 167 | /* (Assuan: "OPTION default-cancel CANCEL"). */ 168 | char *default_cancel; 169 | /* (Assuan: "OPTION default-prompt PROMPT"). */ 170 | char *default_prompt; 171 | /* (Assuan: "OPTION default-pwmngr 172 | SAVE_PASSWORD_WITH_PASSWORD_MANAGER?"). */ 173 | char *default_pwmngr; 174 | 175 | /* Whether we are allowed to read the password from an external 176 | cache. (Assuan: "OPTION allow-external-password-cache") */ 177 | int allow_external_password_cache; 178 | 179 | /* We only try the cache once. */ 180 | int tried_password_cache; 181 | 182 | /* A stable identifier for the key. (Assuan: "SETKEYINFO 183 | KEYINFO".) */ 184 | char *keyinfo; 185 | 186 | /* Whether we may cache the password (according to the user). */ 187 | int may_cache_password; 188 | 189 | /* NOTE: If you add any additional fields to this structure, be sure 190 | to update the initializer in pinentry/pinentry.c!!! */ 191 | 192 | /* For the quality indicator we need to do an inquiry. Thus we need 193 | to save the assuan ctx. */ 194 | void *ctx_assuan; 195 | 196 | }; 197 | typedef struct pinentry *pinentry_t; 198 | 199 | 200 | /* The pinentry command handler type processes the pinentry request 201 | PIN. If PIN->pin is zero, request a confirmation, otherwise a PIN 202 | entry. On confirmation, the function should return TRUE if 203 | confirmed, and FALSE otherwise. On PIN entry, the function should 204 | return -1 if an error occured or the user cancelled the operation 205 | and 1 otherwise. */ 206 | typedef int (*pinentry_cmd_handler_t) (pinentry_t pin); 207 | 208 | /* Start the pinentry event loop. The program will start to process 209 | Assuan commands until it is finished or an error occurs. If an 210 | error occurs, -1 is returned and errno indicates the type of an 211 | error. Otherwise, 0 is returned. */ 212 | int pinentry_loop (void); 213 | 214 | /* The same as above but allows to specify the i/o descriptors. 215 | * infd and outfd will be duplicated in this function so the caller 216 | * still has to close them if necessary. 217 | */ 218 | int pinentry_loop2 (int infd, int outfd); 219 | 220 | 221 | /* Convert the UTF-8 encoded string TEXT to the encoding given in 222 | LC_CTYPE. Return NULL on error. */ 223 | char *pinentry_utf8_to_local (const char *lc_ctype, const char *text); 224 | 225 | /* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With 226 | SECURE set to true, use secure memory for the returned buffer. 227 | Return NULL on error. */ 228 | char *pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure); 229 | 230 | 231 | /* Run a quality inquiry for PASSPHRASE of LENGTH. */ 232 | int pinentry_inq_quality (pinentry_t pin, 233 | const char *passphrase, size_t length); 234 | 235 | /* Try to make room for at least LEN bytes for the pin in the pinentry 236 | PIN. Returns new buffer on success and 0 on failure. */ 237 | char *pinentry_setbufferlen (pinentry_t pin, int len); 238 | 239 | /* Use the buffer at BUFFER for PIN->PIN. BUFFER must be NULL or 240 | allocated using secmem_alloc. LEN is the size of the buffer. If 241 | it is unknown, but BUFFER is a NUL terminated string, you pass 0 to 242 | just use strlen(buffer)+1. */ 243 | void pinentry_setbuffer_use (pinentry_t pin, char *buffer, int len); 244 | 245 | /* Initialize the secure memory subsystem, drop privileges and 246 | return. Must be called early. */ 247 | void pinentry_init (const char *pgmname); 248 | 249 | /* Return true if either DISPLAY is set or ARGV contains the string 250 | "--display". */ 251 | int pinentry_have_display (int argc, char **argv); 252 | 253 | /* Parse the command line options. May exit the program if only help 254 | or version output is requested. */ 255 | void pinentry_parse_opts (int argc, char *argv[]); 256 | 257 | 258 | /* The caller must define this variable to process assuan commands. */ 259 | extern pinentry_cmd_handler_t pinentry_cmd_handler; 260 | 261 | 262 | 263 | 264 | 265 | #ifdef HAVE_W32_SYSTEM 266 | /* Windows declares sleep as obsolete, but provides a definition for 267 | _sleep but non for the still existing sleep. */ 268 | #define sleep(a) _sleep ((a)) 269 | #endif /*HAVE_W32_SYSTEM*/ 270 | 271 | 272 | 273 | #if 0 274 | { 275 | #endif 276 | #ifdef __cplusplus 277 | } 278 | #endif 279 | 280 | #endif /* PINENTRY_H */ 281 | -------------------------------------------------------------------------------- /pinentry/secmem++.h: -------------------------------------------------------------------------------- 1 | /* STL allocator for secmem 2 | * Copyright (C) 2008 Marc Mutz 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | #ifndef __SECMEM_SECMEMPP_H__ 20 | #define __SECMEM_SECMEMPP_H__ 21 | 22 | #include "secmem/memory.h" 23 | #include 24 | 25 | namespace secmem { 26 | 27 | template 28 | class alloc { 29 | public: 30 | // type definitions: 31 | typedef size_t size_type; 32 | typedef ptrdiff_t difference_type; 33 | typedef T* pointer; 34 | typedef const T* const_pointer; 35 | typedef T& reference; 36 | typedef const T& const_reference; 37 | typedef T value_type; 38 | 39 | // rebind 40 | template 41 | struct rebind { 42 | typedef alloc other; 43 | }; 44 | 45 | // address 46 | pointer address( reference value ) const { 47 | return &value; 48 | } 49 | const_pointer address( const_reference value ) const { 50 | return &value; 51 | } 52 | 53 | // (trivial) ctors and dtors 54 | alloc() {} 55 | alloc( const alloc & ) {} 56 | template alloc( const alloc & ) {} 57 | // copy ctor is ok 58 | ~alloc() {} 59 | 60 | // de/allocation 61 | size_type max_size() const { 62 | return secmem_get_max_size(); 63 | } 64 | 65 | pointer allocate( size_type n, void * =0 ) { 66 | return static_cast( secmem_malloc( n * sizeof(T) ) ); 67 | } 68 | 69 | void deallocate( pointer p, size_type ) { 70 | secmem_free( p ); 71 | } 72 | 73 | // de/construct 74 | void construct( pointer p, const T & value ) { 75 | void * loc = p; 76 | new (loc)T(value); 77 | } 78 | void destruct( pointer p ) { 79 | p->~T(); 80 | } 81 | }; 82 | 83 | // equality comparison 84 | template 85 | bool operator==( const alloc &, const alloc & ) { return true; } 86 | template 87 | bool operator!=( const alloc &, const alloc & ) { return false; } 88 | 89 | } 90 | 91 | #endif /* __SECMEM_SECMEMPP_H__ */ 92 | -------------------------------------------------------------------------------- /pinentry/secmem-util.h: -------------------------------------------------------------------------------- 1 | /* This file exists because "util.h" is such a generic name that it is 2 | likely to clash with other such files. */ 3 | #include "util.h" 4 | -------------------------------------------------------------------------------- /pinentry/secmem.c: -------------------------------------------------------------------------------- 1 | /* secmem.c - memory allocation from a secure heap 2 | * Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. 3 | * Copyright (C) 2015 g10 Code GmbH 4 | * 5 | * This file is part of GnuPG. 6 | * 7 | * GnuPG is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * GnuPG is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #if defined(HAVE_MLOCK) || defined(HAVE_MMAP) 28 | # include 29 | # include 30 | # include 31 | # ifdef USE_CAPABILITIES 32 | # include 33 | # endif 34 | #endif 35 | #include 36 | 37 | #include "memory.h" 38 | 39 | #ifdef ORIGINAL_GPG_VERSION 40 | #include "types.h" 41 | #include "util.h" 42 | #else /* ORIGINAL_GPG_VERSION */ 43 | 44 | #include "util.h" 45 | 46 | typedef union { 47 | int a; 48 | short b; 49 | char c[1]; 50 | long d; 51 | #ifdef HAVE_U64_TYPEDEF 52 | u64 e; 53 | #endif 54 | float f; 55 | double g; 56 | } PROPERLY_ALIGNED_TYPE; 57 | 58 | #define log_error log_info 59 | #define log_bug log_fatal 60 | 61 | void 62 | log_info(char *template, ...) 63 | { 64 | va_list args; 65 | 66 | va_start(args, template); 67 | vfprintf(stderr, template, args); 68 | va_end(args); 69 | } 70 | 71 | void 72 | log_fatal(char *template, ...) 73 | { 74 | va_list args; 75 | 76 | va_start(args, template); 77 | vfprintf(stderr, template, args); 78 | va_end(args); 79 | exit(EXIT_FAILURE); 80 | } 81 | 82 | #endif /* ORIGINAL_GPG_VERSION */ 83 | 84 | #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) 85 | # define MAP_ANONYMOUS MAP_ANON 86 | #endif 87 | 88 | #define DEFAULT_POOLSIZE 16384 89 | 90 | typedef struct memblock_struct MEMBLOCK; 91 | struct memblock_struct { 92 | unsigned size; 93 | union { 94 | MEMBLOCK *next; 95 | PROPERLY_ALIGNED_TYPE aligned; 96 | } u; 97 | }; 98 | 99 | 100 | 101 | static void *pool; 102 | static volatile int pool_okay; /* may be checked in an atexit function */ 103 | static int pool_is_mmapped; 104 | static size_t poolsize; /* allocated length */ 105 | static size_t poollen; /* used length */ 106 | static MEMBLOCK *unused_blocks; 107 | static unsigned max_alloced; 108 | static unsigned cur_alloced; 109 | static unsigned max_blocks; 110 | static unsigned cur_blocks; 111 | static int disable_secmem; 112 | static int show_warning; 113 | static int no_warning; 114 | static int suspend_warning; 115 | 116 | 117 | static void 118 | print_warn(void) 119 | { 120 | if( !no_warning ) 121 | log_info("Warning: using insecure memory!\n"); 122 | } 123 | 124 | 125 | static void 126 | lock_pool( void *p, size_t n ) 127 | { 128 | #if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK) 129 | int err; 130 | 131 | cap_set_proc( cap_from_text("cap_ipc_lock+ep") ); 132 | err = mlock( p, n ); 133 | if( err && errno ) 134 | err = errno; 135 | cap_set_proc( cap_from_text("cap_ipc_lock+p") ); 136 | 137 | if( err ) { 138 | if( errno != EPERM 139 | #ifdef EAGAIN /* OpenBSD returns this */ 140 | && errno != EAGAIN 141 | #endif 142 | ) 143 | log_error("can't lock memory: %s\n", strerror(err)); 144 | show_warning = 1; 145 | } 146 | 147 | #elif defined(HAVE_MLOCK) 148 | uid_t uid; 149 | int err; 150 | 151 | uid = getuid(); 152 | 153 | #ifdef HAVE_BROKEN_MLOCK 154 | if( uid ) { 155 | errno = EPERM; 156 | err = errno; 157 | } 158 | else { 159 | err = mlock( p, n ); 160 | if( err && errno ) 161 | err = errno; 162 | } 163 | #else 164 | err = mlock( p, n ); 165 | if( err && errno ) 166 | err = errno; 167 | #endif 168 | 169 | if( uid && !geteuid() ) { 170 | if( setuid( uid ) || getuid() != geteuid() ) 171 | log_fatal("failed to reset uid: %s\n", strerror(errno)); 172 | } 173 | 174 | if( err ) { 175 | if( errno != EPERM 176 | #ifdef EAGAIN /* OpenBSD returns this */ 177 | && errno != EAGAIN 178 | #endif 179 | ) 180 | log_error("can't lock memory: %s\n", strerror(err)); 181 | show_warning = 1; 182 | } 183 | 184 | #else 185 | log_info("Please note that you don't have secure memory on this system\n"); 186 | #endif 187 | } 188 | 189 | 190 | static void 191 | init_pool( size_t n) 192 | { 193 | size_t pgsize; 194 | 195 | poolsize = n; 196 | 197 | if( disable_secmem ) 198 | log_bug("secure memory is disabled"); 199 | 200 | #ifdef HAVE_GETPAGESIZE 201 | pgsize = getpagesize(); 202 | #else 203 | pgsize = 4096; 204 | #endif 205 | 206 | #if HAVE_MMAP 207 | poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1); 208 | # ifdef MAP_ANONYMOUS 209 | pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE, 210 | MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 211 | # else /* map /dev/zero instead */ 212 | { int fd; 213 | 214 | fd = open("/dev/zero", O_RDWR); 215 | if( fd == -1 ) { 216 | log_error("can't open /dev/zero: %s\n", strerror(errno) ); 217 | pool = (void*)-1; 218 | } 219 | else { 220 | pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE, 221 | MAP_PRIVATE, fd, 0); 222 | close (fd); 223 | } 224 | } 225 | # endif 226 | if( pool == (void*)-1 ) 227 | log_info("can't mmap pool of %u bytes: %s - using malloc\n", 228 | (unsigned)poolsize, strerror(errno)); 229 | else { 230 | pool_is_mmapped = 1; 231 | pool_okay = 1; 232 | } 233 | 234 | #endif 235 | if( !pool_okay ) { 236 | pool = malloc( poolsize ); 237 | if( !pool ) 238 | log_fatal("can't allocate memory pool of %u bytes\n", 239 | (unsigned)poolsize); 240 | else 241 | pool_okay = 1; 242 | } 243 | lock_pool( pool, poolsize ); 244 | poollen = 0; 245 | } 246 | 247 | 248 | /* concatenate unused blocks */ 249 | static void 250 | compress_pool(void) 251 | { 252 | /* fixme: we really should do this */ 253 | } 254 | 255 | void 256 | secmem_set_flags( unsigned flags ) 257 | { 258 | int was_susp = suspend_warning; 259 | 260 | no_warning = flags & 1; 261 | suspend_warning = flags & 2; 262 | 263 | /* and now issue the warning if it is not longer suspended */ 264 | if( was_susp && !suspend_warning && show_warning ) { 265 | show_warning = 0; 266 | print_warn(); 267 | } 268 | } 269 | 270 | unsigned 271 | secmem_get_flags(void) 272 | { 273 | unsigned flags; 274 | 275 | flags = no_warning ? 1:0; 276 | flags |= suspend_warning ? 2:0; 277 | return flags; 278 | } 279 | 280 | void 281 | secmem_init( size_t n ) 282 | { 283 | if( !n ) { 284 | #ifdef USE_CAPABILITIES 285 | /* drop all capabilities */ 286 | cap_set_proc( cap_from_text("all-eip") ); 287 | 288 | #elif !defined(HAVE_DOSISH_SYSTEM) 289 | uid_t uid; 290 | 291 | disable_secmem=1; 292 | uid = getuid(); 293 | if( uid != geteuid() ) { 294 | if( setuid( uid ) || getuid() != geteuid() ) 295 | log_fatal("failed to drop setuid\n" ); 296 | } 297 | #endif 298 | } 299 | else { 300 | if( n < DEFAULT_POOLSIZE ) 301 | n = DEFAULT_POOLSIZE; 302 | if( !pool_okay ) 303 | init_pool(n); 304 | else 305 | log_error("Oops, secure memory pool already initialized\n"); 306 | } 307 | } 308 | 309 | 310 | void * 311 | secmem_malloc( size_t size ) 312 | { 313 | MEMBLOCK *mb, *mb2; 314 | int compressed=0; 315 | 316 | if( !pool_okay ) { 317 | log_info( 318 | "operation is not possible without initialized secure memory\n"); 319 | log_info("(you may have used the wrong program for this task)\n"); 320 | exit(2); 321 | } 322 | if( show_warning && !suspend_warning ) { 323 | show_warning = 0; 324 | print_warn(); 325 | } 326 | 327 | /* blocks are always a multiple of 32 */ 328 | size += sizeof(MEMBLOCK); 329 | size = ((size + 31) / 32) * 32; 330 | 331 | retry: 332 | /* try to get it from the used blocks */ 333 | for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next ) 334 | if( mb->size >= size ) { 335 | if( mb2 ) 336 | mb2->u.next = mb->u.next; 337 | else 338 | unused_blocks = mb->u.next; 339 | goto leave; 340 | } 341 | /* allocate a new block */ 342 | if( (poollen + size <= poolsize) ) { 343 | mb = (void*)((char*)pool + poollen); 344 | poollen += size; 345 | mb->size = size; 346 | } 347 | else if( !compressed ) { 348 | compressed=1; 349 | compress_pool(); 350 | goto retry; 351 | } 352 | else 353 | return NULL; 354 | 355 | leave: 356 | cur_alloced += mb->size; 357 | cur_blocks++; 358 | if( cur_alloced > max_alloced ) 359 | max_alloced = cur_alloced; 360 | if( cur_blocks > max_blocks ) 361 | max_blocks = cur_blocks; 362 | 363 | memset (&mb->u.aligned.c, 0, 364 | size - (size_t) &((struct memblock_struct *) 0)->u.aligned.c); 365 | 366 | return &mb->u.aligned.c; 367 | } 368 | 369 | 370 | void * 371 | secmem_realloc( void *p, size_t newsize ) 372 | { 373 | MEMBLOCK *mb; 374 | size_t size; 375 | void *a; 376 | 377 | if (! p) 378 | return secmem_malloc(newsize); 379 | 380 | mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c)); 381 | size = mb->size; 382 | if( newsize < size ) 383 | return p; /* it is easier not to shrink the memory */ 384 | a = secmem_malloc( newsize ); 385 | memcpy(a, p, size); 386 | memset((char*)a+size, 0, newsize-size); 387 | secmem_free(p); 388 | return a; 389 | } 390 | 391 | 392 | void 393 | secmem_free( void *a ) 394 | { 395 | MEMBLOCK *mb; 396 | size_t size; 397 | 398 | if( !a ) 399 | return; 400 | 401 | mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c)); 402 | size = mb->size; 403 | /* This does not make much sense: probably this memory is held in the 404 | * cache. We do it anyway: */ 405 | wipememory2(mb, 0xff, size ); 406 | wipememory2(mb, 0xaa, size ); 407 | wipememory2(mb, 0x55, size ); 408 | wipememory2(mb, 0x00, size ); 409 | mb->size = size; 410 | mb->u.next = unused_blocks; 411 | unused_blocks = mb; 412 | cur_blocks--; 413 | cur_alloced -= size; 414 | } 415 | 416 | int 417 | m_is_secure( const void *p ) 418 | { 419 | return p >= pool && p < (void*)((char*)pool+poolsize); 420 | } 421 | 422 | void 423 | secmem_term() 424 | { 425 | if( !pool_okay ) 426 | return; 427 | 428 | wipememory2( pool, 0xff, poolsize); 429 | wipememory2( pool, 0xaa, poolsize); 430 | wipememory2( pool, 0x55, poolsize); 431 | wipememory2( pool, 0x00, poolsize); 432 | #if HAVE_MMAP 433 | if( pool_is_mmapped ) 434 | munmap( pool, poolsize ); 435 | #endif 436 | pool = NULL; 437 | pool_okay = 0; 438 | poolsize=0; 439 | poollen=0; 440 | unused_blocks=NULL; 441 | } 442 | 443 | 444 | void 445 | secmem_dump_stats() 446 | { 447 | if( disable_secmem ) 448 | return; 449 | fprintf(stderr, 450 | "secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n", 451 | cur_alloced, max_alloced, cur_blocks, max_blocks, 452 | (ulong)poollen, (ulong)poolsize ); 453 | } 454 | 455 | 456 | size_t 457 | secmem_get_max_size (void) 458 | { 459 | return poolsize; 460 | } 461 | -------------------------------------------------------------------------------- /pinentry/util.c: -------------------------------------------------------------------------------- 1 | /* Quintuple Agent 2 | * Copyright (C) 1999 Robert Bihlmeyer 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include 21 | #endif 22 | 23 | #define _GNU_SOURCE 1 24 | 25 | #include 26 | #ifndef HAVE_W32CE_SYSTEM 27 | # include 28 | #endif 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "util.h" 36 | 37 | #ifndef HAVE_DOSISH_SYSTEM 38 | static int uid_set = 0; 39 | static uid_t real_uid, file_uid; 40 | #endif /*!HAVE_DOSISH_SYSTEM*/ 41 | 42 | /* Write DATA of size BYTES to FD, until all is written or an error 43 | occurs. */ 44 | ssize_t 45 | xwrite(int fd, const void *data, size_t bytes) 46 | { 47 | char *ptr; 48 | size_t todo; 49 | ssize_t written = 0; 50 | 51 | for (ptr = (char *)data, todo = bytes; todo; ptr += written, todo -= written) 52 | { 53 | do 54 | written = write (fd, ptr, todo); 55 | while ( 56 | #ifdef HAVE_W32CE_SYSTEM 57 | 0 58 | #else 59 | written == -1 && errno == EINTR 60 | #endif 61 | ); 62 | if (written < 0) 63 | break; 64 | } 65 | return written; 66 | } 67 | 68 | #if 0 69 | extern int debug; 70 | 71 | int 72 | debugmsg(const char *fmt, ...) 73 | { 74 | va_list va; 75 | int ret; 76 | 77 | if (debug) { 78 | va_start(va, fmt); 79 | fprintf(stderr, "\e[4m"); 80 | ret = vfprintf(stderr, fmt, va); 81 | fprintf(stderr, "\e[24m"); 82 | va_end(va); 83 | return ret; 84 | } else 85 | return 0; 86 | } 87 | #endif 88 | 89 | /* initialize uid variables */ 90 | #ifndef HAVE_DOSISH_SYSTEM 91 | static void 92 | init_uids(void) 93 | { 94 | real_uid = getuid(); 95 | file_uid = geteuid(); 96 | uid_set = 1; 97 | } 98 | #endif 99 | 100 | 101 | #if 0 /* Not used. */ 102 | /* lower privileges to the real user's */ 103 | void 104 | lower_privs() 105 | { 106 | if (!uid_set) 107 | init_uids(); 108 | if (real_uid != file_uid) { 109 | #ifdef HAVE_SETEUID 110 | if (seteuid(real_uid) < 0) { 111 | perror("lowering privileges failed"); 112 | exit(EXIT_FAILURE); 113 | } 114 | #else 115 | fprintf(stderr, _("Warning: running q-agent setuid on this system is dangerous\n")); 116 | #endif /* HAVE_SETEUID */ 117 | } 118 | } 119 | #endif /* if 0 */ 120 | 121 | #if 0 /* Not used. */ 122 | /* raise privileges to the effective user's */ 123 | void 124 | raise_privs() 125 | { 126 | assert(real_uid >= 0); /* lower_privs() must be called before this */ 127 | #ifdef HAVE_SETEUID 128 | if (real_uid != file_uid && seteuid(file_uid) < 0) { 129 | perror("Warning: raising privileges failed"); 130 | } 131 | #endif /* HAVE_SETEUID */ 132 | } 133 | #endif /* if 0 */ 134 | 135 | /* drop all additional privileges */ 136 | void 137 | drop_privs() 138 | { 139 | #ifndef HAVE_DOSISH_SYSTEM 140 | if (!uid_set) 141 | init_uids(); 142 | if (real_uid != file_uid) { 143 | if (setuid(real_uid) < 0) { 144 | perror("dropping privileges failed"); 145 | exit(EXIT_FAILURE); 146 | } 147 | file_uid = real_uid; 148 | } 149 | #endif 150 | } 151 | -------------------------------------------------------------------------------- /pinentry/util.h: -------------------------------------------------------------------------------- 1 | /* Quintuple Agent utilities 2 | * Copyright (C) 1999 Robert Bihlmeyer 3 | * Copyright (C) 2003 g10 Code GmbH 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #ifndef _UTIL_H 21 | #define _UTIL_H 22 | 23 | #include 24 | 25 | #ifndef HAVE_BYTE_TYPEDEF 26 | # undef byte 27 | # ifdef __riscos__ 28 | /* Norcroft treats char == unsigned char but char* != unsigned char* */ 29 | typedef char byte; 30 | # else 31 | typedef unsigned char byte; 32 | # endif 33 | # define HAVE_BYTE_TYPEDEF 34 | #endif 35 | 36 | #ifndef HAVE_ULONG_TYPEDEF 37 | # undef ulong 38 | typedef unsigned long ulong; 39 | # define HAVE_ULONG_TYPEDEF 40 | #endif 41 | 42 | 43 | ssize_t xwrite(int, const void *, size_t); /* write until finished */ 44 | int debugmsg(const char *, ...); /* output a debug message if debugging==on */ 45 | void drop_privs(void); /* finally drop privileges */ 46 | 47 | 48 | /* To avoid that a compiler optimizes certain memset calls away, these 49 | macros may be used instead. */ 50 | #define wipememory2(_ptr,_set,_len) do { \ 51 | volatile char *_vptr=(volatile char *)(_ptr); \ 52 | size_t _vlen=(_len); \ 53 | while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ 54 | } while(0) 55 | #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) 56 | #define wipe(_ptr,_len) wipememory2(_ptr,0,_len) 57 | 58 | 59 | 60 | 61 | #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ 62 | *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) 63 | #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) 64 | 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [[ $1 -eq 1 ]]; then 4 | 5 | echo "SETTITLE title 6 | SETPROMPT prompt 7 | SETDESC PROMPT 8 | GETPIN 9 | BYE" | ./pinentry-dmenu 10 | 11 | elif [[ $1 -eq 2 ]]; then 12 | 13 | echo "SETTITLE title 14 | SETPROMPT confirm 15 | SETDESC CONFIRM 16 | confirm 17 | BYE" | ./pinentry-dmenu 18 | 19 | elif [[ $1 -eq 3 ]]; then 20 | 21 | echo "SETTITLE title 22 | SETPROMPT prompt 23 | SETDESC REPEAT 24 | SETREPEAT repeat 25 | GETPIN 26 | BYE" | ./pinentry-dmenu 27 | 28 | else 29 | 30 | echo "SETTITLE title 31 | SETPROMPT prompt 32 | SETDESC PROMPT 33 | GETPIN 34 | SETPROMPT confirm 35 | SETDESC CONFIRM 36 | confirm 37 | SETPROMPT repeat 38 | SETDESC REPEAT 39 | SETREPEAT 40 | GETPIN 41 | BYE" | ./pinentry-dmenu 42 | 43 | fi 44 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void * 10 | ecalloc(size_t nmemb, size_t size) 11 | { 12 | void *p; 13 | 14 | if (!(p = calloc(nmemb, size))) 15 | die("calloc:"); 16 | return p; 17 | } 18 | 19 | void 20 | die(const char *fmt, ...) { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 28 | fputc(' ', stderr); 29 | perror(NULL); 30 | } else { 31 | fputc('\n', stderr); 32 | } 33 | 34 | exit(1); 35 | } 36 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 6 | 7 | void die(const char *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | --------------------------------------------------------------------------------