Line | Count | Source (jump to first uncovered line) |
1 | | #include "rar.hpp" |
2 | | #include "log.cpp" |
3 | | |
4 | | static MESSAGE_TYPE MsgStream=MSG_STDOUT; |
5 | | static RAR_CHARSET RedirectCharset=RCH_DEFAULT; |
6 | | static bool ProhibitInput=false; |
7 | | static bool ConsoleOutputPresent=false; |
8 | | |
9 | | static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; |
10 | | |
11 | | #ifdef _WIN_ALL |
12 | | static bool IsRedirected(DWORD nStdHandle) |
13 | | { |
14 | | HANDLE hStd=GetStdHandle(nStdHandle); |
15 | | DWORD Mode; |
16 | | return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0; |
17 | | } |
18 | | #endif |
19 | | |
20 | | |
21 | | void InitConsole() |
22 | 0 | { |
23 | | #ifdef _WIN_ALL |
24 | | // We want messages like file names or progress percent to be printed |
25 | | // immediately. Use only in Windows, in Unix they can cause wprintf %ls |
26 | | // to fail with non-English strings. |
27 | | setbuf(stdout,NULL); |
28 | | setbuf(stderr,NULL); |
29 | | |
30 | | // Detect if output is redirected and set output mode properly. |
31 | | // We do not want to send Unicode output to files and especially to pipes |
32 | | // like '|more', which cannot handle them correctly in Windows. |
33 | | // In Unix console output is UTF-8 and it is handled correctly |
34 | | // when redirecting, so no need to perform any adjustments. |
35 | | StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE); |
36 | | StderrRedirected=IsRedirected(STD_ERROR_HANDLE); |
37 | | StdinRedirected=IsRedirected(STD_INPUT_HANDLE); |
38 | | #ifdef _MSC_VER |
39 | | if (!StdoutRedirected) |
40 | | _setmode(_fileno(stdout), _O_U16TEXT); |
41 | | if (!StderrRedirected) |
42 | | _setmode(_fileno(stderr), _O_U16TEXT); |
43 | | #endif |
44 | | #elif defined(_UNIX) |
45 | | StdoutRedirected=!isatty(fileno(stdout)); |
46 | 0 | StderrRedirected=!isatty(fileno(stderr)); |
47 | 0 | StdinRedirected=!isatty(fileno(stdin)); |
48 | 0 | #endif |
49 | 0 | } |
50 | | |
51 | | |
52 | | void SetConsoleMsgStream(MESSAGE_TYPE MsgStream) |
53 | 0 | { |
54 | 0 | ::MsgStream=MsgStream; |
55 | 0 | } |
56 | | |
57 | | |
58 | | void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset) |
59 | 0 | { |
60 | 0 | ::RedirectCharset=RedirectCharset; |
61 | 0 | } |
62 | | |
63 | | |
64 | | void ProhibitConsoleInput() |
65 | 0 | { |
66 | 0 | ProhibitInput=true; |
67 | 0 | } |
68 | | |
69 | | |
70 | | #ifndef SILENT |
71 | | static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) |
72 | | { |
73 | | ConsoleOutputPresent=true; |
74 | | |
75 | | // No need for PrintfPrepareFmt here, vwstrprintf calls it. |
76 | | std::wstring s=vwstrprintf(fmt,arglist); |
77 | | |
78 | | ReplaceEsc(s); |
79 | | |
80 | | #ifdef _WIN_ALL |
81 | | if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) |
82 | | { |
83 | | HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); |
84 | | DWORD Written; |
85 | | if (RedirectCharset==RCH_UNICODE) |
86 | | WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL); |
87 | | else |
88 | | { |
89 | | // Avoid Unicode for redirect in Windows, it does not work with pipes. |
90 | | std::string MsgA; |
91 | | if (RedirectCharset==RCH_UTF8) |
92 | | WideToUtf(s,MsgA); |
93 | | else |
94 | | WideToChar(s,MsgA); |
95 | | if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) |
96 | | CharToOemA(&MsgA[0],&MsgA[0]); // Console tools like 'more' expect OEM encoding. |
97 | | |
98 | | // We already converted \n to \r\n above, so we use WriteFile instead |
99 | | // of C library to avoid unnecessary additional conversion. |
100 | | WriteFile(hOut,MsgA.data(),(DWORD)MsgA.size(),&Written,NULL); |
101 | | } |
102 | | return; |
103 | | } |
104 | | // MSVC2008 vfwprintf writes every character to console separately |
105 | | // and it is too slow. We use direct WriteConsole call instead. |
106 | | HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); |
107 | | DWORD Written; |
108 | | WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL); |
109 | | #else |
110 | | fputws(s.c_str(),dest); |
111 | | // We do not use setbuf(NULL) in Unix (see comments in InitConsole). |
112 | | fflush(dest); |
113 | | #endif |
114 | | } |
115 | | |
116 | | |
117 | | void mprintf(const wchar *fmt,...) |
118 | | { |
119 | | if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) |
120 | | return; |
121 | | |
122 | | fflush(stderr); // Ensure proper message order. |
123 | | |
124 | | va_list arglist; |
125 | | va_start(arglist,fmt); |
126 | | FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout; |
127 | | cvt_wprintf(dest,fmt,arglist); |
128 | | va_end(arglist); |
129 | | } |
130 | | #endif |
131 | | |
132 | | |
133 | | #ifndef SILENT |
134 | | void eprintf(const wchar *fmt,...) |
135 | | { |
136 | | if (MsgStream==MSG_NULL) |
137 | | return; |
138 | | |
139 | | fflush(stdout); // Ensure proper message order. |
140 | | |
141 | | va_list arglist; |
142 | | va_start(arglist,fmt); |
143 | | cvt_wprintf(stderr,fmt,arglist); |
144 | | va_end(arglist); |
145 | | } |
146 | | #endif |
147 | | |
148 | | |
149 | | #ifndef SILENT |
150 | | static void QuitIfInputProhibited() |
151 | | { |
152 | | // We cannot handle user prompts if -si is used to read file or archive data |
153 | | // from stdin. |
154 | | if (ProhibitInput) |
155 | | { |
156 | | mprintf(St(MStdinNoInput)); |
157 | | ErrHandler.Exit(RARX_FATAL); |
158 | | } |
159 | | } |
160 | | |
161 | | |
162 | | static void GetPasswordText(std::wstring &Str) |
163 | | { |
164 | | QuitIfInputProhibited(); |
165 | | if (StdinRedirected) |
166 | | getwstr(Str); // Read from pipe or redirected file. |
167 | | else |
168 | | { |
169 | | #ifdef _WIN_ALL |
170 | | HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); |
171 | | DWORD ConInMode; |
172 | | GetConsoleMode(hConIn,&ConInMode); |
173 | | SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT. |
174 | | |
175 | | std::vector<wchar> Buf(MAXPASSWORD); |
176 | | |
177 | | // We prefer ReadConsole to ReadFile, so we can read Unicode input. |
178 | | DWORD Read=0; |
179 | | ReadConsole(hConIn,Buf.data(),(DWORD)Buf.size()-1,&Read,NULL); |
180 | | Buf[Read]=0; |
181 | | Str=Buf.data(); |
182 | | cleandata(Buf.data(),Buf.size()*sizeof(Buf[0])); |
183 | | |
184 | | SetConsoleMode(hConIn,ConInMode); |
185 | | |
186 | | // 2023.03.12: Previously we checked for presence of "\n" in entered |
187 | | // passwords, supposing that truncated strings do not include it. |
188 | | // We did it to read the rest of excessively long string, so it is not |
189 | | // read later as the second password for -p switch. But this "\n" check |
190 | | // doesn't seem to work in Windows 10 anymore and "\r" is present even |
191 | | // in truncated strings. Also we increased MAXPASSWORD, so it is larger |
192 | | // than MAXPASSWORD_RAR. Thus we removed this check as not working |
193 | | // and not that necessary. Low level FlushConsoleInputBuffer doesn't help |
194 | | // for high level ReadConsole, which in line input mode seems to store |
195 | | // the rest of string in its own internal buffer. |
196 | | #else |
197 | | std::vector<char> StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters. |
198 | | #ifdef __VMS |
199 | | fgets(StrA.data(),StrA.size()-1,stdin); |
200 | | #elif defined(__sun) |
201 | | strncpyz(StrA.data(),getpassphrase(""),StrA.size()); |
202 | | #else |
203 | | strncpyz(StrA.data(),getpass(""),StrA.size()); |
204 | | #endif |
205 | | CharToWide(StrA.data(),Str); |
206 | | cleandata(StrA.data(),StrA.size()*sizeof(StrA[0])); |
207 | | #endif |
208 | | } |
209 | | RemoveLF(Str); |
210 | | } |
211 | | #endif |
212 | | |
213 | | |
214 | | #ifndef SILENT |
215 | | bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password) |
216 | | { |
217 | | if (!StdinRedirected) |
218 | | uiAlarm(UIALARM_QUESTION); |
219 | | |
220 | | while (true) |
221 | | { |
222 | | // if (!StdinRedirected) |
223 | | if (Type==UIPASSWORD_GLOBAL) |
224 | | eprintf(L"\n%s: ",St(MAskPsw)); |
225 | | else |
226 | | eprintf(St(MAskPswFor),FileName.c_str()); |
227 | | |
228 | | std::wstring PlainPsw; |
229 | | GetPasswordText(PlainPsw); |
230 | | if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL) |
231 | | return false; |
232 | | if (PlainPsw.size()>=MAXPASSWORD) |
233 | | { |
234 | | PlainPsw.erase(MAXPASSWORD-1); |
235 | | uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); |
236 | | } |
237 | | if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) |
238 | | { |
239 | | eprintf(St(MReAskPsw)); |
240 | | std::wstring CmpStr; |
241 | | GetPasswordText(CmpStr); |
242 | | if (CmpStr.empty() || PlainPsw!=CmpStr) |
243 | | { |
244 | | eprintf(St(MNotMatchPsw)); |
245 | | cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0])); |
246 | | cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0])); |
247 | | continue; |
248 | | } |
249 | | cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0])); |
250 | | } |
251 | | Password->Set(PlainPsw.c_str()); |
252 | | cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0])); |
253 | | break; |
254 | | } |
255 | | return true; |
256 | | } |
257 | | #endif |
258 | | |
259 | | |
260 | | #ifndef SILENT |
261 | | void getwstr(std::wstring &str) |
262 | | { |
263 | | // Print buffered prompt title function before waiting for input. |
264 | | fflush(stderr); |
265 | | |
266 | | QuitIfInputProhibited(); |
267 | | |
268 | | str.clear(); |
269 | | |
270 | | const size_t MaxRead=MAXPATHSIZE; // Large enough to read a file name. |
271 | | |
272 | | #if defined(_WIN_ALL) |
273 | | // fgetws does not work well with non-English text in Windows, |
274 | | // so we do not use it. |
275 | | if (StdinRedirected) // ReadConsole does not work if redirected. |
276 | | { |
277 | | // fgets does not work well with pipes in Windows in our test. |
278 | | // Let's use files. |
279 | | std::vector<char> StrA(MaxRead*4); // Up to 4 UTF-8 characters per wchar_t. |
280 | | File SrcFile; |
281 | | SrcFile.SetHandleType(FILE_HANDLESTD); |
282 | | SrcFile.SetLineInputMode(true); |
283 | | int ReadSize=SrcFile.Read(&StrA[0],StrA.size()-1); |
284 | | if (ReadSize<=0) |
285 | | { |
286 | | // Looks like stdin is a null device. We can enter to infinite loop |
287 | | // calling Ask() or set an empty password, so let's better exit. |
288 | | ErrHandler.ReadError(L"stdin"); |
289 | | } |
290 | | StrA[ReadSize]=0; |
291 | | |
292 | | // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR, |
293 | | // such as send passwords, we get OEM encoding by default, unless we |
294 | | // use "chcp" in console. But we avoid OEM to ANSI conversion, |
295 | | // because we also want to handle ANSI files redirection correctly, |
296 | | // like "rar ... < ansifile.txt". |
297 | | CharToWide(&StrA[0],str); |
298 | | cleandata(&StrA[0],StrA.size()); // We can use this function to enter passwords. |
299 | | } |
300 | | else |
301 | | { |
302 | | std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t. |
303 | | DWORD SizeToRead=(DWORD)Buf.size()-1; |
304 | | |
305 | | // ReadConsole fails in Windows 7 for requested input exceeding 30 KB. |
306 | | // Not certain about Windows 8, so check for Windows 10 here. |
307 | | if (WinNT()<=WNT_W10) |
308 | | SizeToRead=Min(SizeToRead,0x4000); |
309 | | |
310 | | DWORD ReadSize=0; |
311 | | if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],SizeToRead,&ReadSize,NULL)==0) |
312 | | ErrHandler.ReadError(L"stdin"); // Unknown user input, safer to abort. |
313 | | Buf[ReadSize]=0; |
314 | | str=Buf.data(); |
315 | | } |
316 | | #else |
317 | | std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t. |
318 | | if (fgetws(&Buf[0],Buf.size(),stdin)==NULL) |
319 | | ErrHandler.ReadError(L"stdin"); // Avoid infinite Ask() loop. |
320 | | str=Buf.data(); |
321 | | #endif |
322 | | RemoveLF(str); |
323 | | } |
324 | | #endif |
325 | | |
326 | | |
327 | | #ifndef SILENT |
328 | | // We allow this function to return 0 in case of invalid input, |
329 | | // because it might be convenient to press Enter to some not dangerous |
330 | | // prompts like "insert disk with next volume". We should call this function |
331 | | // again in case of 0 in dangerous prompt such as overwriting file. |
332 | | int Ask(const wchar *AskStr) |
333 | | { |
334 | | uiAlarm(UIALARM_QUESTION); |
335 | | |
336 | | const uint MaxItems=10; |
337 | | wchar Item[MaxItems][40]; |
338 | | uint ItemKeyPos[MaxItems],NumItems=0; |
339 | | |
340 | | for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_')) |
341 | | { |
342 | | wchar *CurItem=Item[NumItems]; |
343 | | wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0])); |
344 | | wchar *EndItem=wcschr(CurItem,'_'); |
345 | | if (EndItem!=nullptr) |
346 | | *EndItem=0; |
347 | | uint KeyPos=0,CurKey; |
348 | | while ((CurKey=CurItem[KeyPos])!=0) |
349 | | { |
350 | | bool Found=false; |
351 | | for (uint I=0;I<NumItems && !Found;I++) |
352 | | if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey)) |
353 | | Found=true; |
354 | | if (!Found && CurKey!=' ') |
355 | | break; |
356 | | KeyPos++; |
357 | | } |
358 | | ItemKeyPos[NumItems]=KeyPos; |
359 | | NumItems++; |
360 | | } |
361 | | |
362 | | for (uint I=0;I<NumItems;I++) |
363 | | { |
364 | | eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", "); |
365 | | uint KeyPos=ItemKeyPos[I]; |
366 | | for (uint J=0;J<KeyPos;J++) |
367 | | eprintf(L"%c",Item[I][J]); |
368 | | eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]); |
369 | | } |
370 | | eprintf(L" "); |
371 | | std::wstring Str; |
372 | | getwstr(Str); |
373 | | wchar Ch=toupperw(Str[0]); |
374 | | for (uint I=0;I<NumItems;I++) |
375 | | if (Ch==Item[I][ItemKeyPos[I]]) |
376 | | return I+1; |
377 | | return 0; |
378 | | } |
379 | | #endif |
380 | | |
381 | | |
382 | | static bool IsCommentUnsafe(const std::wstring &Data) |
383 | 563 | { |
384 | 421k | for (size_t I=0;I<Data.size();I++) |
385 | 420k | if (Data[I]==27 && Data[I+1]=='[') |
386 | 4.23k | for (size_t J=I+2;J<Data.size();J++) |
387 | 4.22k | { |
388 | | // Return true for <ESC>[{key};"{string}"p used to redefine |
389 | | // a keyboard key on some terminals. |
390 | 4.22k | if (Data[J]=='\"') |
391 | 2 | return true; |
392 | 4.22k | if (!IsDigit(Data[J]) && Data[J]!=';') |
393 | 2.56k | break; |
394 | 4.22k | } |
395 | 561 | return false; |
396 | 563 | } |
397 | | |
398 | | |
399 | | void OutComment(const std::wstring &Comment) |
400 | 563 | { |
401 | 563 | if (IsCommentUnsafe(Comment)) |
402 | 2 | return; |
403 | 561 | const size_t MaxOutSize=0x400; |
404 | 1.49k | for (size_t I=0;I<Comment.size();I+=MaxOutSize) |
405 | 937 | { |
406 | 937 | size_t CopySize=Min(MaxOutSize,Comment.size()-I); |
407 | 937 | mprintf(L"%s",Comment.substr(I,CopySize).c_str()); |
408 | 937 | } |
409 | 561 | mprintf(L"\n"); |
410 | 561 | } |
411 | | |
412 | | |
413 | | bool IsConsoleOutputPresent() |
414 | 0 | { |
415 | 0 | return ConsoleOutputPresent; |
416 | 0 | } |